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