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