BGPCEP-564: Support multiple cluster IDs
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPPeer.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.base.Objects;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.net.InetAddresses;
21 import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture;
23 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 import javax.annotation.Nullable;
35 import javax.annotation.concurrent.GuardedBy;
36 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
37 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
38 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
39 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
40 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
41 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
42 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
43 import org.opendaylight.protocol.bgp.parser.BGPError;
44 import org.opendaylight.protocol.bgp.parser.impl.message.update.LocalPreferenceAttributeParser;
45 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
47 import org.opendaylight.protocol.bgp.rib.impl.state.BGPPeerStateImpl;
48 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateProvider;
49 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
50 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
51 import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
52 import org.opendaylight.protocol.bgp.rib.spi.Peer;
53 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
54 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
55 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
56 import org.opendaylight.protocol.bgp.rib.spi.state.BGPAfiSafiState;
57 import org.opendaylight.protocol.bgp.rib.spi.state.BGPErrorHandlingState;
58 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
59 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
60 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
61 import org.opendaylight.protocol.concepts.AbstractRegistration;
62 import org.opendaylight.protocol.util.Ipv4Util;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev171207.ipv4.prefixes.DestinationIpv4Builder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev171207.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev171207.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev171207.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationIpv4CaseBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.Update;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.AttributesBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.update.message.Nlri;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.BgpAddPathTableType;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.BgpTableType;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.RouteRefresh;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.SendReceive;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.mp.capabilities.add.path.capability.AddressFamilies;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.update.attributes.MpReachNlri;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.update.attributes.MpReachNlriBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.update.attributes.MpUnreachNlri;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.update.attributes.MpUnreachNlriBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev171207.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev171207.BgpPeerRpcService;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev171207.PeerContext;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerRole;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.PeerKey;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.AdjRibOut;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AddressFamily;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
96 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
97 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
98 import org.opendaylight.yangtools.yang.binding.Notification;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102 /**
103  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
104  * RIB actions.
105  */
106 public class BGPPeer extends BGPPeerStateImpl implements BGPRouteEntryImportParameters,
107         BGPSessionListener, Peer, TransactionChainListener {
108     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
109     private final ClusterIdentifier clusterId;
110
111     private Set<TablesKey> tables = Collections.emptySet();
112     private final RIB rib;
113     private final String name;
114     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
115     private final RpcProviderRegistry rpcRegistry;
116     private final PeerRole peerRole;
117     private final InstanceIdentifier<AdjRibOut> peerRibOutIId;
118     private final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
119             .rev171207.bgp.rib.rib.Peer, PeerKey> peerIId;
120     @GuardedBy("this")
121     private AbstractRegistration trackerRegistration;
122     private final PeerId peerId;
123     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
124             = CacheBuilder.newBuilder()
125             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
126                 @Override
127                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
128                     return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
129                 }
130             });
131
132     @GuardedBy("this")
133     private BGPSession session;
134     @GuardedBy("this")
135     private byte[] rawIdentifier;
136     @GuardedBy("this")
137     private DOMTransactionChain chain;
138     @GuardedBy("this")
139     private AdjRibInWriter ribWriter;
140     @GuardedBy("this")
141     private EffectiveRibInWriter effRibInWriter;
142     private RoutedRpcRegistration<BgpPeerRpcService> rpcRegistration;
143     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
144
145     public BGPPeer(
146             final IpAddress neighborAddress,
147             final String peerGroupName,
148             final RIB rib,
149             final PeerRole role,
150             final ClusterIdentifier clusterId,
151             final RpcProviderRegistry rpcRegistry,
152             final Set<TablesKey> afiSafisAdvertized,
153             final Set<TablesKey> afiSafisGracefulAdvertized) {
154         super(rib.getInstanceIdentifier(), peerGroupName, neighborAddress, afiSafisAdvertized,
155                 afiSafisGracefulAdvertized);
156         this.peerRole = role;
157         this.rib = requireNonNull(rib);
158         this.clusterId = clusterId;
159         this.name = Ipv4Util.toStringIP(neighborAddress);
160         this.rpcRegistry = rpcRegistry;
161         this.peerId = RouterIds.createPeerId(neighborAddress);
162         this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
163                 .yang.bgp.rib.rev171207.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
164         this.peerRibOutIId = this.peerIId.child(AdjRibOut.class);
165         this.chain = rib.createPeerDOMChain(this);
166     }
167
168     BGPPeer(
169             final IpAddress neighborAddress,
170             final RIB rib,
171             final PeerRole role,
172             final RpcProviderRegistry rpcRegistry,
173             final Set<TablesKey> afiSafisAdvertized,
174             final Set<TablesKey> afiSafisGracefulAdvertized) {
175         this(neighborAddress, null, rib, role, null, rpcRegistry, afiSafisAdvertized,
176                 afiSafisGracefulAdvertized);
177     }
178
179
180     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
181         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
182             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
183             attributesBuilder.setCNextHop(mpReach.getCNextHop());
184             return attributesBuilder.build();
185         }
186         return attrs;
187     }
188
189     /**
190      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
191      *
192      * @param message Update message containing prefixes in NLRI
193      * @return MpReachNlri with prefixes from the nlri field
194      */
195     private static MpReachNlri prefixesToMpReach(final Update message) {
196         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
197                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
198                 .collect(Collectors.toList());
199         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
200                 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
201                 new AdvertizedRoutesBuilder().setDestinationType(
202                         new DestinationIpv4CaseBuilder().setDestinationIpv4(
203                                 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
204         if (message.getAttributes() != null) {
205             b.setCNextHop(message.getAttributes().getCNextHop());
206         }
207         return b.build();
208     }
209
210     /**
211      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
212      *
213      * @param message            Update message containing withdrawn routes
214      * @param isAnyNlriAnnounced isAnyNlriAnnounced
215      * @return MpUnreachNlri with prefixes from the withdrawn routes field
216      */
217     private static MpUnreachNlri prefixesToMpUnreach(final Update message, final boolean isAnyNlriAnnounced) {
218         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
219         message.getWithdrawnRoutes().forEach(w -> {
220
221             Optional<Nlri> nlriAnounced = Optional.empty();
222             if (isAnyNlriAnnounced) {
223                 nlriAnounced = message.getNlri().stream().filter(n -> Objects.equal(n.getPrefix(), w.getPrefix())
224                         && Objects.equal(n.getPathId(), w.getPathId()))
225                         .findAny();
226             }
227             if (!nlriAnounced.isPresent()) {
228                 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build());
229             }
230         });
231         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
232                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
233                         .urn.opendaylight.params.xml.ns.yang.bgp.inet.rev171207.update.attributes.mp.unreach.nlri
234                         .withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
235                         new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
236     }
237
238     private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
239         return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
240                         af.getSafi()), BgpAddPathTableType::getSendReceive)));
241     }
242
243     public synchronized void instantiateServiceInstance() {
244         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this.chain);
245         setActive(true);
246     }
247
248     // FIXME ListenableFuture<?> should be used once closeServiceInstance uses wildcard too
249     @Override
250     public synchronized ListenableFuture<Void> close() {
251         final ListenableFuture<Void> future = releaseConnection();
252         this.chain.close();
253         setActive(false);
254         return future;
255     }
256
257     @Override
258     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
259         if (!(msg instanceof Update) && !(msg instanceof RouteRefresh)) {
260             LOG.info("Ignoring unhandled message class {}", msg.getClass());
261             return;
262         }
263         if (msg instanceof Update) {
264             onUpdateMessage((Update) msg);
265         } else {
266             onRouteRefreshMessage((RouteRefresh) msg);
267         }
268     }
269
270     private void onRouteRefreshMessage(final RouteRefresh message) {
271         final Class<? extends AddressFamily> rrAfi = message.getAfi();
272         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
273
274         final TablesKey key = new TablesKey(rrAfi, rrSafi);
275         final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
276         if (listener != null) {
277             listener.close();
278             this.adjRibOutListenerSet.remove(key);
279             createAdjRibOutListener(key, listener.isMpSupported());
280         } else {
281             LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
282         }
283     }
284
285     /**
286      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
287      *
288      * @param message Update message
289      */
290     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
291         if (MessageUtil.isAnyNlriPresent(message)) {
292             final Attributes attrs = message.getAttributes();
293             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
294                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
295                         BGPError.WELL_KNOWN_ATTR_MISSING,
296                         new byte[]{LocalPreferenceAttributeParser.TYPE});
297             }
298         }
299     }
300
301     /**
302      * Process Update message received.
303      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
304      *
305      * @param message Update message
306      */
307     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
308         checkMandatoryAttributesPresence(message);
309
310         // update AdjRibs
311         final Attributes attrs = message.getAttributes();
312         MpReachNlri mpReach;
313         final boolean isAnyNlriAnnounced = message.getNlri() != null;
314         if (isAnyNlriAnnounced) {
315             mpReach = prefixesToMpReach(message);
316         } else {
317             mpReach = MessageUtil.getMpReachNlri(attrs);
318         }
319         if (mpReach != null) {
320             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
321         }
322         MpUnreachNlri mpUnreach;
323         if (message.getWithdrawnRoutes() != null) {
324             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
325         } else {
326             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
327         }
328         if (mpUnreach != null) {
329             this.ribWriter.removeRoutes(mpUnreach);
330         }
331     }
332
333     @Override
334     public synchronized void onSessionUp(final BGPSession session) {
335         this.session = session;
336         if (this.session instanceof BGPSessionStateProvider) {
337             ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
338         }
339         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
340         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
341         final List<BgpTableType> advertizedGracefulRestartTableTypes = session.getAdvertisedGracefulRestartTableTypes();
342         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
343                 advertizedTableTypes, addPathTablesType);
344         this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
345         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
346                 .collect(Collectors.toSet());
347         this.tables = ImmutableSet.copyOf(setTables);
348
349         setAdvertizedGracefulRestartTableTypes(advertizedGracefulRestartTableTypes.stream()
350                 .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
351         this.addPathTableMaps = ImmutableMap.copyOf(mapTableTypesFamilies(addPathTablesType));
352         this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
353
354         for (final TablesKey key : this.tables) {
355             createAdjRibOutListener(key, true);
356         }
357
358         addBgp4Support();
359
360         this.effRibInWriter = EffectiveRibInWriter.create(this, this.rib, this.rib.createPeerChain(this),
361                 this.peerIId, this.tables);
362
363
364         registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
365         this.ribWriter = this.ribWriter.transform(this.peerId, this.rib.getRibSupportContext(), this.tables,
366                 this.addPathTableMaps);
367
368         if (this.rpcRegistry != null) {
369             this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
370                     new BgpPeerRpc(this, session, this.tables));
371             final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
372                     .rev171207.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
373                     .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib
374                              .rib.Peer.class, new PeerKey(this.peerId));
375             this.rpcRegistration.registerPath(PeerContext.class, path);
376         }
377     }
378
379     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
380     private synchronized void addBgp4Support() {
381         final TablesKey key = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
382         if (!this.tables.contains(key)) {
383             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
384             newSet.add(key);
385             this.tables = ImmutableSet.copyOf(newSet);
386             createAdjRibOutListener(key, false);
387         }
388     }
389
390     private synchronized void createAdjRibOutListener(final TablesKey key,
391             final boolean mpSupport) {
392         final RIBSupport ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
393
394         // not particularly nice
395         if (ribSupport != null && this.session instanceof BGPSessionImpl) {
396             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
397             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
398                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
399                     this.rib.getService(), limiter, mpSupport);
400             this.adjRibOutListenerSet.put(key, adjRibOut);
401             registerPrefixesSentCounter(key, adjRibOut);
402         }
403     }
404
405     private synchronized ListenableFuture<Void> cleanup() {
406         // FIXME: BUG-196: support graceful
407         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
408         this.adjRibOutListenerSet.clear();
409         if (this.effRibInWriter != null) {
410             this.effRibInWriter.close();
411         }
412         this.tables = Collections.emptySet();
413         this.addPathTableMaps = Collections.emptyMap();
414         if (this.ribWriter != null) {
415             return this.ribWriter.removePeer();
416         }
417         return Futures.immediateFuture(null);
418     }
419
420     @Override
421     public synchronized void onSessionDown(final BGPSession session, final Exception e) {
422         if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
423             LOG.info("Session with peer {} went down", this.name);
424         } else {
425             LOG.info("Session with peer {} went down", this.name, e);
426         }
427         releaseConnection();
428     }
429
430     @Override
431     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
432         LOG.info("Session with peer {} terminated: {}", this.name, cause);
433         releaseConnection();
434     }
435
436     @Override
437     public String toString() {
438         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
439     }
440
441     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
442         toStringHelper.add("name", this.name);
443         toStringHelper.add("tables", this.tables);
444         return toStringHelper;
445     }
446
447     @Override
448     public String getName() {
449         return this.name;
450     }
451
452     @Override
453     public synchronized ListenableFuture<Void> releaseConnection() {
454         if (this.rpcRegistration != null) {
455             this.rpcRegistration.close();
456         }
457         closeRegistration();
458         final ListenableFuture<Void> future = cleanup();
459
460         if (this.session != null) {
461             try {
462                 this.session.close();
463             } catch (final Exception e) {
464                 LOG.warn("Error closing session with peer", e);
465             }
466             this.session = null;
467         }
468         resetState();
469         return future;
470     }
471
472     private synchronized void closeRegistration() {
473         if (this.trackerRegistration != null) {
474             this.trackerRegistration.close();
475             this.trackerRegistration = null;
476         }
477     }
478
479     @Override
480     public synchronized byte[] getRawIdentifier() {
481         return Arrays.copyOf(this.rawIdentifier, this.rawIdentifier.length);
482     }
483
484     @Override
485     public PeerId getPeerId() {
486         return this.peerId;
487     }
488
489     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
490     @Override
491     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
492         return this.addPathTableMaps.get(tableKey);
493     }
494
495     @Override
496     public boolean supportsTable(final TablesKey tableKey) {
497         return this.tables.contains(tableKey);
498     }
499
500     @Override
501     public PeerRole getRole() {
502         return this.peerRole;
503     }
504
505     @Override
506     public ClusterIdentifier getClusterId() {
507         return this.clusterId;
508     }
509
510     @Override
511     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
512         return this.tablesIId.getUnchecked(tablesKey);
513     }
514
515     @Override
516     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
517             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
518         LOG.error("Transaction chain failed.", cause);
519         this.chain.close();
520         //FIXME
521         /*
522         this.chain = this.rib.createPeerDOMChain(this);
523         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this.chain);
524         releaseConnection();*/
525     }
526
527     @Override
528     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
529         LOG.debug("Transaction chain {} successful.", chain);
530     }
531
532     @Override
533     public synchronized void markUptodate(final TablesKey tablesKey) {
534         this.ribWriter.markTableUptodate(tablesKey);
535     }
536
537     @Override
538     public BGPErrorHandlingState getBGPErrorHandlingState() {
539         return this;
540     }
541
542     @Override
543     public BGPAfiSafiState getBGPAfiSafiState() {
544         return this;
545     }
546
547     @Override
548     public synchronized BGPSessionState getBGPSessionState() {
549         if (this.session instanceof BGPSessionStateProvider) {
550             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
551         }
552         return null;
553     }
554
555     @Override
556     public synchronized BGPTimersState getBGPTimersState() {
557         if (this.session instanceof BGPSessionStateProvider) {
558             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
559         }
560         return null;
561     }
562
563     @Override
564     public synchronized BGPTransportState getBGPTransportState() {
565         if (this.session instanceof BGPSessionStateProvider) {
566             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
567         }
568         return null;
569     }
570
571     @Override
572     public PeerRole getFromPeerRole() {
573         return getRole();
574     }
575
576     @Override
577     public PeerId getFromPeerId() {
578         return getPeerId();
579     }
580
581     @Override
582     public ClusterIdentifier getFromClusterId() {
583         return getClusterId();
584     }
585 }