Move EffectiveRibInWriter to Binding independent
[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                 case DISAPPEARED:
261                     processDeleteRouteTables(child, childIdentifier, ribSupport, routesPath);
262                     tx.delete(LogicalDatastoreType.OPERATIONAL, routesPath);
263                     LOG.debug("Route deleted. routeId={}", routesPath);
264                     break;
265                 case UNMODIFIED:
266                     // No-op
267                     break;
268                 case SUBTREE_MODIFIED:
269                     processModifiedRouteTables(child, childIdentifier,tx, ribSupport, routesPath, childDataAfter);
270                     break;
271                 case APPEARED:
272                 case WRITE:
273                     writeRouteTables(child, childIdentifier, tx, ribSupport, routesPath, childDataAfter);
274                     break;
275                 default:
276                     LOG.warn("Ignoring unhandled child {}", child);
277                     break;
278             }
279         }
280     }
281
282     private void processDeleteRouteTables(
283         final DataTreeCandidateNode child,
284         final PathArgument childIdentifier,
285         final RIBSupport ribSupport,
286         final YangInstanceIdentifier routesPath) {
287         if (TABLE_ROUTES.equals(childIdentifier)) {
288             final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
289             for (final DataTreeCandidateNode route : changedRoutes) {
290                 handleRouteTarget(ModificationType.DELETE, ribSupport, routesPath.getParent(),
291                     route.getDataBefore().orElse(null));
292                 final TablesKey tablesKey = ribSupport.getTablesKey();
293                 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
294             }
295         }
296     }
297
298     private void processModifiedRouteTables(
299         final DataTreeCandidateNode child,
300         final PathArgument childIdentifier,
301         final DOMDataWriteTransaction tx,
302         final RIBSupport ribSupport,
303         final YangInstanceIdentifier routesPath,
304         final Optional<NormalizedNode<?, ?>> childDataAfter) {
305         if (TABLE_ROUTES.equals(childIdentifier)) {
306             final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
307             for (final DataTreeCandidateNode route : changedRoutes) {
308                 processRoute(tx, ribSupport, routesPath.getParent(), route);
309             }
310         } else {
311             tx.put(LogicalDatastoreType.OPERATIONAL, routesPath, childDataAfter.get());
312         }
313     }
314
315     private void writeRouteTables(
316         final DataTreeCandidateNode child,
317         final PathArgument childIdentifier,
318         final DOMDataWriteTransaction tx,
319         final RIBSupport ribSupport,
320         final YangInstanceIdentifier routesPath,
321         final Optional<NormalizedNode<?, ?>> childDataAfter) {
322         if (TABLE_ROUTES.equals(childIdentifier)) {
323             final Collection<DataTreeCandidateNode> changedRoutes = ribSupport.changedRoutes(child);
324             if (!changedRoutes.isEmpty()) {
325                 tx.put(LogicalDatastoreType.OPERATIONAL, routesPath, childDataAfter.get());
326                 // Routes are special, as they may end up being filtered. The previous put conveniently
327                 // ensured that we have them in at target, so a subsequent delete will not fail :)
328                 for (final DataTreeCandidateNode route : changedRoutes) {
329                     processRoute(tx, ribSupport, routesPath.getParent(), route);
330                 }
331             }
332         }
333     }
334
335     private void processRoute(
336         final DOMDataWriteTransaction tx,
337         final RIBSupport ribSupport,
338         final YangInstanceIdentifier routesPath,
339         final DataTreeCandidateNode route) {
340         LOG.debug("Process route {}", route.getIdentifier());
341         final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
342         final TablesKey tablesKey = ribSupport.getTablesKey();
343         switch (route.getModificationType()) {
344             case DELETE:
345             case DISAPPEARED:
346                 deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey);
347                 break;
348             case UNMODIFIED:
349                 // No-op
350                 break;
351             case APPEARED:
352             case SUBTREE_MODIFIED:
353             case WRITE:
354                 CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
355                 // Lookup per-table attributes from RIBSupport
356                 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(),
357                     ribSupport.routeAttributesIdentifier()).orElse(null);
358                 final Attributes routeAttrs = ribSupport.attributeFromContainerNode(advertisedAttrs);
359                 final Optional<Attributes> optEffAtt;
360                 // In case we want to add LLGR_STALE we do not process route through policies since it may be
361                 // considered as received with LLGR_STALE from peer which is not true.
362                 final boolean longLivedStale = false;
363                 if (longLivedStale) {
364                     // LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
365                     final List<Communities> effCommunities = routeAttrs.getCommunities();
366                     if (effCommunities != null && effCommunities.contains(CommunityUtil.NO_LLGR)) {
367                         deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey);
368                         return;
369                     }
370                     optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
371                 } else {
372                     final Class<? extends AfiSafiType> afiSafiType
373                         = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get();
374                     optEffAtt = this.ribPolicies
375                         .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType);
376                 }
377                 if (!optEffAtt.isPresent()) {
378                     deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey);
379                     return;
380                 }
381                 final NormalizedNode<?, ?> routeChanged = route.getDataAfter().get();
382                 handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeChanged);
383                 tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeChanged);
384                 CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
385
386                 final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
387                 final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, optEffAtt.get());
388                 tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
389                 break;
390             default:
391                 LOG.warn("Ignoring unhandled route {}", route);
392                 break;
393         }
394     }
395
396     private void deleteRoute(
397         final DOMDataWriteTransaction tx,
398         final RIBSupport ribSupport,
399         final YangInstanceIdentifier routeIdPath,
400         final NormalizedNode<?, ?> route,
401         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(
409         final ModificationType modificationType,
410         final RIBSupport ribSupport,
411         final YangInstanceIdentifier routeIdPath,
412         final NormalizedNode<?, ?> route) {
413         if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
414             final RouteTargetConstrainRoute rtc =
415                 (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
416             final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
417             if (ModificationType.DELETE == modificationType) {
418                 if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
419                     this.rtCache.uncacheRoute(rtc);
420                 }
421                 this.rtMemberships.remove(rtMembership);
422             } else {
423                 if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
424                     this.rtCache.cacheRoute(rtc);
425                 }
426                 this.rtMemberships.add(rtMembership);
427             }
428             this.rtMembershipsUpdated = true;
429         }
430     }
431
432     @SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD")
433     private static Attributes wrapLongLivedStale(final Attributes attrs) {
434         if (attrs == null) {
435             return STALE_LLGR_ATTRIBUTES;
436         }
437
438         final List<Communities> oldCommunities = attrs.getCommunities();
439         final List<Communities> newCommunities;
440         if (oldCommunities != null) {
441             if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
442                 return attrs;
443             }
444             newCommunities = StaleCommunities.create(oldCommunities);
445         } else {
446             newCommunities = STALE_LLGR_COMMUNUTIES;
447         }
448
449         return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329
450                 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
451     }
452
453     private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
454         return this.effRibTables.node(Tables.QNAME).node(tableKey);
455     }
456
457     @Override
458     public synchronized void close() {
459         if (this.reg != null) {
460             this.reg.close();
461             this.reg = null;
462         }
463         if (this.submitted != null) {
464             try {
465                 this.submitted.get();
466             } catch (final InterruptedException | ExecutionException throwable) {
467                 LOG.error("Write routes failed", throwable);
468             }
469         }
470         if (this.chain != null) {
471             this.chain.close();
472             this.chain = null;
473         }
474         this.prefixesReceived.values().forEach(LongAdder::reset);
475         this.prefixesInstalled.values().forEach(LongAdder::reset);
476     }
477
478     @Override
479     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
480         final LongAdder counter = this.prefixesReceived.get(tablesKey);
481         if (counter == null) {
482             return 0;
483         }
484         return counter.longValue();
485     }
486
487     @Override
488     public Set<TablesKey> getTableKeys() {
489         return ImmutableSet.copyOf(this.prefixesReceived.keySet());
490     }
491
492     @Override
493     public boolean isSupported(final TablesKey tablesKey) {
494         return this.prefixesReceived.containsKey(tablesKey);
495     }
496
497     @Override
498     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
499         final LongAdder counter = this.prefixesInstalled.get(tablesKey);
500         if (counter == null) {
501             return 0;
502         }
503         return counter.longValue();
504     }
505
506     @Override
507     public long getTotalPrefixesInstalled() {
508         return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
509     }
510 }