Further cleanup of EffectiveRibInWriter
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / EffectiveRibInWriter.java
1 /*
2  * Copyright (c) 2015 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 com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN;
13 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.EFFRIBIN;
14 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ROUTES;
15 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES;
16
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.util.concurrent.FluentFuture;
21 import com.google.common.util.concurrent.FutureCallback;
22 import com.google.common.util.concurrent.MoreExecutors;
23 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.atomic.LongAdder;
31 import javax.annotation.Nonnull;
32 import javax.annotation.concurrent.GuardedBy;
33 import javax.annotation.concurrent.NotThreadSafe;
34 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
35 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
37 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
38 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
39 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
40 import org.opendaylight.mdsal.common.api.CommitInfo;
41 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
42 import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
43 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
47 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
48 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
49 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
50 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
51 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
52 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
53 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.RouteTargetMembeshipUtil;
54 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.AfiSafiType;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.attributes.Communities;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.RouteTargetConstrainSubsequentAddressFamily;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.routes.route.target.constrain.routes.RouteTargetConstrainRoute;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
65 import org.opendaylight.yangtools.concepts.ListenerRegistration;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
69 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
72 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
73 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 /**
79  * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
80  * routes in the context of the advertising peer's role and applies the inbound policy.
81  *
82  * <p>
83  * Inbound policy is applied as follows:
84  *
85  * <p>
86  * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
87  * 2) check if a route is admissible based on attributes attached to it, as well as the
88  * advertising peer's role
89  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
90  */
91 @NotThreadSafe
92 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
93         AutoCloseable, ClusteredDOMDataTreeChangeListener {
94
95     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
96     private static final TablesKey IVP4_VPN_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
97             MplsLabeledVpnSubsequentAddressFamily.class);
98     private static final TablesKey IVP6_VPN_TABLE_KEY = new TablesKey(Ipv6AddressFamily.class,
99             MplsLabeledVpnSubsequentAddressFamily.class);
100     private static final ImmutableList<Communities> STALE_LLGR_COMMUNUTIES = ImmutableList.of(
101         StaleCommunities.STALE_LLGR);
102     private static final Attributes STALE_LLGR_ATTRIBUTES = new org.opendaylight.yang.gen.v1.urn.opendaylight.params
103             .xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder()
104             .setCommunities(STALE_LLGR_COMMUNUTIES)
105             .build();
106
107     private final RIBSupportContextRegistry registry;
108     private final YangInstanceIdentifier peerIId;
109     private final YangInstanceIdentifier effRibTables;
110     private final DOMDataTreeChangeService service;
111     private final List<RouteTarget> rtMemberships;
112     private final RibOutRefresh vpnTableRefresher;
113     private final ClientRouteTargetContrainCache rtCache;
114     private ListenerRegistration<?> reg;
115     private DOMTransactionChain chain;
116     private final Map<TablesKey, LongAdder> prefixesReceived;
117     private final Map<TablesKey, LongAdder> prefixesInstalled;
118     private final BGPRibRoutingPolicy ribPolicies;
119     private final BGPRouteEntryImportParameters peerImportParameters;
120     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
121     @GuardedBy("this")
122     private FluentFuture<? extends CommitInfo> submitted;
123     private boolean rtMembershipsUpdated;
124
125     EffectiveRibInWriter(
126             final BGPRouteEntryImportParameters peer,
127             final RIB rib,
128             final DOMTransactionChain chain,
129             final YangInstanceIdentifier peerIId,
130             final Set<TablesKey> tables,
131             final BGPTableTypeRegistryConsumer tableTypeRegistry,
132             final List<RouteTarget> rtMemberships,
133             final ClientRouteTargetContrainCache rtCache) {
134         this.registry = requireNonNull(rib.getRibSupportContext());
135         this.chain = requireNonNull(chain);
136         this.peerIId = requireNonNull(peerIId);
137         this.effRibTables = this.peerIId.node(EFFRIBIN);
138         this.prefixesInstalled = buildPrefixesTables(tables);
139         this.prefixesReceived = buildPrefixesTables(tables);
140         this.ribPolicies = requireNonNull(rib.getRibPolicies());
141         this.service = requireNonNull(rib.getService());
142         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
143         this.peerImportParameters = peer;
144         this.rtMemberships = rtMemberships;
145         this.rtCache = rtCache;
146         this.vpnTableRefresher = rib;
147     }
148
149     public void init() {
150         final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
151             this.peerIId.node(ADJRIBIN).node(TABLES));
152         LOG.debug("Registered Effective RIB on {}", this.peerIId);
153         this.reg = requireNonNull(this.service).registerDataTreeChangeListener(treeId, this);
154     }
155
156     private static Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
157         final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
158         tables.forEach(table -> b.put(table, new LongAdder()));
159         return b.build();
160     }
161
162     @Override
163     public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
164         if (this.chain == null) {
165             LOG.trace("Chain closed. Ignoring Changes : {}", changes);
166             return;
167         }
168
169         LOG.trace("Data changed called to effective RIB. Change : {}", changes);
170         DOMDataWriteTransaction tx = null;
171         for (final DataTreeCandidate tc : changes) {
172             final YangInstanceIdentifier rootPath = tc.getRootPath();
173             final DataTreeCandidateNode root = tc.getRootNode();
174             for (final DataTreeCandidateNode table : root.getChildNodes()) {
175                 if (tx == null) {
176                     tx = this.chain.newWriteOnlyTransaction();
177                 }
178                 changeDataTree(tx, rootPath, root, table);
179             }
180         }
181
182         if (tx != null) {
183             final FluentFuture<? extends CommitInfo> future = tx.commit();
184             this.submitted = future;
185             future.addCallback(new FutureCallback<CommitInfo>() {
186                 @Override
187                 public void onSuccess(final CommitInfo result) {
188                     LOG.trace("Successful commit");
189                 }
190
191                 @Override
192                 public void onFailure(final Throwable trw) {
193                     LOG.error("Failed commit", trw);
194                 }
195             }, MoreExecutors.directExecutor());
196         }
197
198         //Refresh VPN Table if RT Memberships were updated
199         if (this.rtMembershipsUpdated) {
200             this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
201             this.vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
202             this.rtMembershipsUpdated = false;
203         }
204     }
205
206     @GuardedBy("this")
207     private void changeDataTree(final DOMDataWriteTransaction tx, final YangInstanceIdentifier rootPath,
208             final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
209         final PathArgument lastArg = table.getIdentifier();
210         verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(),
211             rootPath);
212         final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
213         final RIBSupportContext ribContext = this.registry.getRIBSupportContext(tableKey);
214         if (ribContext == null) {
215             LOG.warn("Table {} is not supported, ignoring event", tableKey);
216             return;
217         }
218
219         final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
220         final ModificationType modificationType = root.getModificationType();
221         LOG.debug("Effective table {} modification type {}", effectiveTablePath, modificationType);
222         switch (modificationType) {
223             case DISAPPEARED:
224             case DELETE:
225                 deleteTable(tx, ribContext, effectiveTablePath, table);
226                 break;
227             case APPEARED:
228             case WRITE:
229                 writeTable(tx, ribContext, effectiveTablePath, table);
230                 break;
231             case SUBTREE_MODIFIED:
232                 modifyTable(tx, ribContext, effectiveTablePath, table);
233                 break;
234             case UNMODIFIED:
235                 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
236                 break;
237             default:
238                 LOG.warn("Ignoring unhandled root {}", table);
239                 break;
240         }
241     }
242
243     private void deleteTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
244             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
245         processTableChildren(tx, ribContext.getRibSupport(), effectiveTablePath, table.getChildNodes());
246         LOG.debug("Delete Effective Table {}", effectiveTablePath);
247         tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
248     }
249
250     private void modifyTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
251             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
252         LOG.debug("Modify Effective Table {}", effectiveTablePath);
253         processTableChildren(tx, ribContext.getRibSupport(), effectiveTablePath, table.getChildNodes());
254     }
255
256     private void writeTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
257             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
258         LOG.debug("Write Effective Table {}", effectiveTablePath);
259         ribContext.createEmptyTableStructure(tx, effectiveTablePath);
260         processTableChildren(tx, ribContext.getRibSupport(), effectiveTablePath, table.getChildNodes());
261     }
262
263     private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
264             final YangInstanceIdentifier effectiveTablePath, final Collection<DataTreeCandidateNode> children) {
265         for (final DataTreeCandidateNode child : children) {
266             final PathArgument childIdentifier = child.getIdentifier();
267             final ModificationType childModType = child.getModificationType();
268             final Optional<NormalizedNode<?, ?>> childDataAfter = child.getDataAfter();
269             LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, childModType,
270                 childDataAfter, child.getDataBefore());
271             final YangInstanceIdentifier routesPath = effectiveTablePath.node(childIdentifier);
272             switch (childModType) {
273                 case DELETE:
274                 case DISAPPEARED:
275                     processTableChildrenDelete(child, childIdentifier, tx, ribSupport, routesPath);
276                     break;
277                 case UNMODIFIED:
278                     // No-op
279                     break;
280                 case SUBTREE_MODIFIED:
281                     processModifiedRouteTables(child, childIdentifier, tx, ribSupport, routesPath, childDataAfter);
282                     break;
283                 case APPEARED:
284                 case WRITE:
285                     writeRouteTables(child, childIdentifier, tx, ribSupport, routesPath, childDataAfter);
286                     break;
287                 default:
288                     LOG.warn("Ignoring unhandled child {}", child);
289                     break;
290             }
291         }
292     }
293
294     private void processTableChildrenDelete(final DataTreeCandidateNode child, final PathArgument childIdentifier,
295             final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
296             final YangInstanceIdentifier routesPath) {
297         if (ROUTES.equals(childIdentifier)) {
298             for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
299                 final Optional<NormalizedNode<?, ?>> routeBefore = route.getDataBefore();
300                 if (routeBefore.isPresent()) {
301                     final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
302                     handleRouteTarget(ModificationType.DELETE, ribSupport, routePath, routeBefore.get());
303                     final TablesKey tablesKey = ribSupport.getTablesKey();
304                     CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
305                 }
306             }
307         }
308         tx.delete(LogicalDatastoreType.OPERATIONAL, routesPath);
309     }
310
311     private void processModifiedRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier,
312             final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
313             final YangInstanceIdentifier routesPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
314         if (ROUTES.equals(childIdentifier)) {
315             for (final DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
316                 processRoute(tx, ribSupport, routesPath.getParent(), route);
317             }
318         } else {
319             tx.put(LogicalDatastoreType.OPERATIONAL, routesPath, childDataAfter.get());
320         }
321     }
322
323     private void writeRouteTables(final DataTreeCandidateNode child, final PathArgument childIdentifier,
324             final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
325             final YangInstanceIdentifier routesPath, final Optional<NormalizedNode<?, ?>> childDataAfter) {
326         if (ROUTES.equals(childIdentifier)) {
327             final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
328             if (!changedRoutes.isEmpty()) {
329                 tx.put(LogicalDatastoreType.OPERATIONAL, routesPath, childDataAfter.get());
330                 // Routes are special, as they may end up being filtered. The previous put conveniently
331                 // ensured that we have them in at target, so a subsequent delete will not fail :)
332                 for (final DataTreeCandidateNode route : changedRoutes) {
333                     processRoute(tx, ribSupport, routesPath.getParent(), route);
334                 }
335             }
336         }
337     }
338
339     private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
340             final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
341         LOG.debug("Process route {}", route.getIdentifier());
342         final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
343         final TablesKey tablesKey = ribSupport.getTablesKey();
344         switch (route.getModificationType()) {
345             case DELETE:
346             case DISAPPEARED:
347                 deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey);
348                 break;
349             case UNMODIFIED:
350                 // No-op
351                 break;
352             case APPEARED:
353             case SUBTREE_MODIFIED:
354             case WRITE:
355                 CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
356                 // Lookup per-table attributes from RIBSupport
357                 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(),
358                     ribSupport.routeAttributesIdentifier()).orElse(null);
359                 final Attributes routeAttrs = ribSupport.attributeFromContainerNode(advertisedAttrs);
360                 final Optional<Attributes> optEffAtt;
361                 // In case we want to add LLGR_STALE we do not process route through policies since it may be
362                 // considered as received with LLGR_STALE from peer which is not true.
363                 final boolean longLivedStale = false;
364                 if (longLivedStale) {
365                     // LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
366                     final List<Communities> effCommunities = routeAttrs.getCommunities();
367                     if (effCommunities != null && effCommunities.contains(CommunityUtil.NO_LLGR)) {
368                         deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey);
369                         return;
370                     }
371                     optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
372                 } else {
373                     final Class<? extends AfiSafiType> afiSafiType
374                         = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get();
375                     optEffAtt = this.ribPolicies
376                         .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType);
377                 }
378                 if (!optEffAtt.isPresent()) {
379                     deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey);
380                     return;
381                 }
382                 final NormalizedNode<?, ?> routeChanged = route.getDataAfter().get();
383                 handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeChanged);
384                 tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeChanged);
385                 CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
386
387                 final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
388                 final Attributes attToStore = optEffAtt.get();
389                 if(!attToStore.equals(routeAttrs)) {
390                     final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, attToStore);
391                     tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
392                 }
393                 break;
394             default:
395                 LOG.warn("Ignoring unhandled route {}", route);
396                 break;
397         }
398     }
399
400     private void deleteRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
401             final YangInstanceIdentifier routeIdPath, final NormalizedNode<?, ?> route, final TablesKey tablesKey) {
402         handleRouteTarget(ModificationType.DELETE, ribSupport, routeIdPath, route);
403         tx.delete(LogicalDatastoreType.OPERATIONAL, routeIdPath);
404         LOG.debug("Route deleted. routeId={}", routeIdPath);
405         CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
406     }
407
408     private void handleRouteTarget(final ModificationType modificationType, final RIBSupport<?, ?, ?, ?> ribSupport,
409             final YangInstanceIdentifier routeIdPath, final NormalizedNode<?, ?> route) {
410         if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
411             final RouteTargetConstrainRoute rtc =
412                 (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
413             final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
414             if (ModificationType.DELETE == modificationType) {
415                 if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
416                     this.rtCache.uncacheRoute(rtc);
417                 }
418                 this.rtMemberships.remove(rtMembership);
419             } else {
420                 if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
421                     this.rtCache.cacheRoute(rtc);
422                 }
423                 this.rtMemberships.add(rtMembership);
424             }
425             this.rtMembershipsUpdated = true;
426         }
427     }
428
429     @SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD")
430     private static Attributes wrapLongLivedStale(final Attributes attrs) {
431         if (attrs == null) {
432             return STALE_LLGR_ATTRIBUTES;
433         }
434
435         final List<Communities> oldCommunities = attrs.getCommunities();
436         final List<Communities> newCommunities;
437         if (oldCommunities != null) {
438             if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
439                 return attrs;
440             }
441             newCommunities = StaleCommunities.create(oldCommunities);
442         } else {
443             newCommunities = STALE_LLGR_COMMUNUTIES;
444         }
445
446         return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329
447                 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
448     }
449
450     private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
451         return this.effRibTables.node(TABLES).node(tableKey);
452     }
453
454     @Override
455     public synchronized void close() {
456         if (this.reg != null) {
457             this.reg.close();
458             this.reg = null;
459         }
460         if (this.submitted != null) {
461             try {
462                 this.submitted.get();
463             } catch (final InterruptedException | ExecutionException throwable) {
464                 LOG.error("Write routes failed", throwable);
465             }
466         }
467         if (this.chain != null) {
468             this.chain.close();
469             this.chain = null;
470         }
471         this.prefixesReceived.values().forEach(LongAdder::reset);
472         this.prefixesInstalled.values().forEach(LongAdder::reset);
473     }
474
475     @Override
476     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
477         final LongAdder counter = this.prefixesReceived.get(tablesKey);
478         if (counter == null) {
479             return 0;
480         }
481         return counter.longValue();
482     }
483
484     @Override
485     public Set<TablesKey> getTableKeys() {
486         return ImmutableSet.copyOf(this.prefixesReceived.keySet());
487     }
488
489     @Override
490     public boolean isSupported(final TablesKey tablesKey) {
491         return this.prefixesReceived.containsKey(tablesKey);
492     }
493
494     @Override
495     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
496         final LongAdder counter = this.prefixesInstalled.get(tablesKey);
497         if (counter == null) {
498             return 0;
499         }
500         return counter.longValue();
501     }
502
503     @Override
504     public long getTotalPrefixesInstalled() {
505         return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
506     }
507 }