Fix EffectiveRibInWriter.writeTable()
[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
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.util.concurrent.FluentFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Optional;
23 import java.util.Set;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.atomic.LongAdder;
26 import javax.annotation.Nonnull;
27 import javax.annotation.concurrent.GuardedBy;
28 import javax.annotation.concurrent.NotThreadSafe;
29 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
30 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
32 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
33 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
34 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
35 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
36 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
37 import org.opendaylight.mdsal.common.api.CommitInfo;
38 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
39 import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
40 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
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.Route;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibIn;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
67 import org.opendaylight.yangtools.concepts.ListenerRegistration;
68 import org.opendaylight.yangtools.yang.binding.ChildOf;
69 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
70 import org.opendaylight.yangtools.yang.binding.DataObject;
71 import org.opendaylight.yangtools.yang.binding.Identifiable;
72 import org.opendaylight.yangtools.yang.binding.Identifier;
73 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
74 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
76 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
77 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80
81 /**
82  * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
83  * routes in the context of the advertising peer's role and applies the inbound policy.
84  *
85  * <p>
86  * Inbound policy is applied as follows:
87  *
88  * <p>
89  * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
90  * 2) check if a route is admissible based on attributes attached to it, as well as the
91  * advertising peer's role
92  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
93  */
94 @NotThreadSafe
95 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
96         AutoCloseable, ClusteredDataTreeChangeListener<Tables> {
97
98     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
99     static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
100     private static final TablesKey IVP4_VPN_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
101             MplsLabeledVpnSubsequentAddressFamily.class);
102     private static final TablesKey IVP6_VPN_TABLE_KEY = new TablesKey(Ipv6AddressFamily.class,
103             MplsLabeledVpnSubsequentAddressFamily.class);
104     private static final ImmutableList<Communities> STALE_LLGR_COMMUNUTIES = ImmutableList.of(
105         StaleCommunities.STALE_LLGR);
106     private static final Attributes STALE_LLGR_ATTRIBUTES = new org.opendaylight.yang.gen.v1.urn.opendaylight.params
107             .xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder()
108             .setCommunities(STALE_LLGR_COMMUNUTIES)
109             .build();
110
111     private final RIBSupportContextRegistry registry;
112     private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
113     private final InstanceIdentifier<EffectiveRibIn> effRibTables;
114     private final DataBroker databroker;
115     private final List<RouteTarget> rtMemberships;
116     private final RibOutRefresh vpnTableRefresher;
117     private final ClientRouteTargetContrainCache rtCache;
118     private ListenerRegistration<?> reg;
119     private BindingTransactionChain chain;
120     private final Map<TablesKey, LongAdder> prefixesReceived;
121     private final Map<TablesKey, LongAdder> prefixesInstalled;
122     private final BGPRibRoutingPolicy ribPolicies;
123     private final BGPRouteEntryImportParameters peerImportParameters;
124     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
125     @GuardedBy("this")
126     private FluentFuture<? extends CommitInfo> submitted;
127     private boolean rtMembershipsUpdated;
128
129     EffectiveRibInWriter(
130             final BGPRouteEntryImportParameters peer,
131             final RIB rib,
132             final BindingTransactionChain chain,
133             final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
134             final Set<TablesKey> tables,
135             final BGPTableTypeRegistryConsumer tableTypeRegistry,
136             final List<RouteTarget> rtMemberships,
137             final ClientRouteTargetContrainCache rtCache) {
138         this.registry = requireNonNull(rib.getRibSupportContext());
139         this.chain = requireNonNull(chain);
140         this.peerIId = requireNonNull(peerIId);
141         this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
142         this.prefixesInstalled = buildPrefixesTables(tables);
143         this.prefixesReceived = buildPrefixesTables(tables);
144         this.ribPolicies = requireNonNull(rib.getRibPolicies());
145         this.databroker = requireNonNull(rib.getDataBroker());
146         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
147         this.peerImportParameters = peer;
148         this.rtMemberships = rtMemberships;
149         this.rtCache = rtCache;
150         this.vpnTableRefresher = rib;
151     }
152
153     public void init() {
154         final DataTreeIdentifier<Tables> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
155                 this.peerIId.child(AdjRibIn.class).child(Tables.class));
156         LOG.debug("Registered Effective RIB on {}", this.peerIId);
157         this.reg = requireNonNull(this.databroker).registerDataTreeChangeListener(treeId, this);
158     }
159
160     private static Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
161         final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
162         tables.forEach(table -> b.put(table, new LongAdder()));
163         return b.build();
164     }
165
166     @Override
167     public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Tables>> changes) {
168         if (this.chain == null) {
169             LOG.trace("Chain closed. Ignoring Changes : {}", changes);
170             return;
171         }
172
173         LOG.trace("Data changed called to effective RIB. Change : {}", changes);
174         if (!changes.isEmpty()) {
175             processModifications(changes);
176         }
177
178         //Refresh VPN Table if RT Memberships were updated
179         if (this.rtMembershipsUpdated) {
180             this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
181             this.vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
182             this.rtMembershipsUpdated = false;
183         }
184     }
185
186     @GuardedBy("this")
187     @SuppressWarnings("unchecked")
188     private void processModifications(final Collection<DataTreeModification<Tables>> changes) {
189         final WriteTransaction tx = this.chain.newWriteOnlyTransaction();
190         for (final DataTreeModification<Tables> tc : changes) {
191             final DataObjectModification<Tables> table = tc.getRootNode();
192             final DataObjectModification.ModificationType modificationType = table.getModificationType();
193             switch (modificationType) {
194                 case DELETE:
195                     final Tables removeTable = table.getDataBefore();
196                     final TablesKey tableKey = removeTable.key();
197                     final KeyedInstanceIdentifier<Tables, TablesKey> effectiveTablePath = this.effRibTables
198                             .child(Tables.class, tableKey);
199                     LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType);
200                     tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
201                     CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
202                     break;
203                 case SUBTREE_MODIFIED:
204                     final Tables before = table.getDataBefore();
205                     final Tables after = table.getDataAfter();
206                     final TablesKey tk = after.key();
207                     LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
208                             tk, modificationType, after, before);
209
210                     final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
211                             = this.effRibTables.child(Tables.class, tk);
212                     final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
213                     if (ribSupport == null) {
214                         break;
215                     }
216
217                     final DataObjectModification<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
218                         .rib.rev180329.rib.tables.Attributes> adjRibAttrsChanged = table.getModifiedChildContainer(
219                             org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329
220                                 .rib.tables.Attributes.class);
221                     if (adjRibAttrsChanged != null) {
222                         tx.put(LogicalDatastoreType.OPERATIONAL,
223                             tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
224                                 .rib.rev180329.rib.tables.Attributes.class), adjRibAttrsChanged.getDataAfter());
225                     }
226
227                     final DataObjectModification routesChangesContainer = table.getModifiedChildContainer(
228                         ribSupport.routesCaseClass(), ribSupport.routesContainerClass());
229
230                     if (routesChangesContainer == null) {
231                         break;
232                     }
233                     updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
234                     break;
235                 case WRITE:
236                     writeTable(tx, table);
237                     break;
238                 default:
239                     LOG.warn("Ignoring unhandled root {}", table);
240                     break;
241             }
242         }
243
244         final FluentFuture<? extends CommitInfo> future = tx.commit();
245         this.submitted = future;
246         future.addCallback(new FutureCallback<CommitInfo>() {
247             @Override
248             public void onSuccess(final CommitInfo result) {
249                 LOG.trace("Successful commit");
250             }
251
252             @Override
253             public void onFailure(final Throwable trw) {
254                 LOG.error("Failed commit", trw);
255             }
256         }, MoreExecutors.directExecutor());
257     }
258
259     @SuppressWarnings("unchecked")
260     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
261         R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void updateRoutes(
262             final WriteTransaction tx,
263             final TablesKey tableKey, final RIBSupport<C, S, R, I> ribSupport,
264             final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
265             final Collection<DataObjectModification<R>> routeChanges) {
266
267         Class<? extends AfiSafiType> afiSafiType = null;
268         for (final DataObjectModification<R> routeChanged : routeChanges) {
269             final PathArgument routeChangeId = routeChanged.getIdentifier();
270             verify(routeChangeId instanceof IdentifiableItem, "Route change %s has invalid identifier %s",
271                 routeChanged, routeChangeId);
272             final I routeKey = ((IdentifiableItem<R, I>) routeChangeId).getKey();
273
274             switch (routeChanged.getModificationType()) {
275                 case SUBTREE_MODIFIED:
276                 case WRITE:
277                     if (afiSafiType == null) {
278                         afiSafiType = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get();
279                     }
280
281                     writeRoutes(tx, tableKey, afiSafiType, ribSupport, tablePath, routeKey,
282                         routeChanged.getDataAfter(), false);
283                     break;
284                 case DELETE:
285                     final InstanceIdentifier<R> routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
286                     deleteRoutes(routeIID, routeChanged.getDataBefore(), tx);
287                     break;
288             }
289         }
290     }
291
292     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
293             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void writeRoutes(
294             final WriteTransaction tx, final TablesKey tk, final Class<? extends AfiSafiType> afiSafiType,
295             final RIBSupport<C, S, R, I> ribSupport, final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
296             final I routeKey, final R route, final boolean longLivedStale) {
297         final InstanceIdentifier<R> routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
298         CountersUtil.increment(this.prefixesReceived.get(tk), tk);
299
300         final Attributes routeAttrs = route.getAttributes();
301         final Optional<Attributes> optEffAtt;
302         // In case we want to add LLGR_STALE we do not process route through policies since it may be
303         // considered as received with LLGR_STALE from peer which is not true.
304         if (longLivedStale) {
305             // LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
306             final List<Communities> effCommunities = routeAttrs.getCommunities();
307             if (effCommunities != null && effCommunities.contains(CommunityUtil.NO_LLGR)) {
308                 deleteRoutes(routeIID, route, tx);
309                 return;
310             }
311             optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
312         } else {
313             optEffAtt = this.ribPolicies.applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType);
314         }
315         if (!optEffAtt.isPresent()) {
316             deleteRoutes(routeIID, route, tx);
317             return;
318         }
319
320         final Optional<RouteTarget> rtMembership = RouteTargetMembeshipUtil.getRT(route);
321         if (rtMembership.isPresent()) {
322             final RouteTarget rt = rtMembership.get();
323             if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
324                 this.rtCache.cacheRoute(route);
325             }
326             this.rtMemberships.add(rt);
327             this.rtMembershipsUpdated = true;
328         }
329         CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
330         tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route);
331         tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), optEffAtt.get());
332     }
333
334     private static Attributes wrapLongLivedStale(final Attributes attrs) {
335         if (attrs == null) {
336             return STALE_LLGR_ATTRIBUTES;
337         }
338
339         final List<Communities> oldCommunities = attrs.getCommunities();
340         final List<Communities> newCommunities;
341         if (oldCommunities != null) {
342             if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
343                 return attrs;
344             }
345             newCommunities = StaleCommunities.create(oldCommunities);
346         } else {
347             newCommunities = STALE_LLGR_COMMUNUTIES;
348         }
349
350         return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329
351                 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
352     }
353
354     private <R extends Route> void deleteRoutes(final InstanceIdentifier<R> routeIID,
355             final R route, final WriteTransaction tx) {
356         deleteRT(route);
357         tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
358     }
359
360     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
361             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void writeTable(
362                     final WriteTransaction tx, final DataObjectModification<Tables> table) {
363
364         final Tables newTable = table.getDataAfter();
365         if (newTable == null) {
366             final Tables oldTable = table.getDataBefore();
367             if (oldTable != null) {
368                 final TablesKey tableKey = oldTable.key();
369                 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath = tablePath(tableKey);
370                 LOG.trace("Delete table at {}", tablePath);
371                 tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath);
372
373                 final RIBSupport<C, S, R, I> ribSupport = this.registry.getRIBSupport(tableKey);
374                 if (ribSupport != null) {
375                     final Routes oldRoutes = oldTable.getRoutes();
376                     if (oldRoutes != null) {
377                         for (R route : ribSupport.extractAdjRibInRoutes(oldRoutes)) {
378                             deleteRT(route);
379                         }
380                     }
381                 }
382             }
383             return;
384         }
385
386         final TablesKey tableKey = newTable.key();
387         final KeyedInstanceIdentifier<Tables, TablesKey> tablePath = tablePath(tableKey);
388
389         // Create an empty table
390         LOG.trace("Create Empty table at {}", tablePath);
391         tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
392             .withKey(tableKey).setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
393             .setAttributes(newTable.getAttributes()).build());
394
395         final RIBSupport<C, S, R, I> ribSupport = this.registry.getRIBSupport(tableKey);
396         if (ribSupport == null) {
397             LOG.trace("No RIB support for {}", tableKey);
398             return;
399         }
400
401         writeTableRoutes(tx, tableKey, ribSupport, tablePath, newTable);
402     }
403
404     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
405             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void writeTableRoutes(
406                     final WriteTransaction tx, final TablesKey tableKey, final RIBSupport<C, S, R, I> ribSupport,
407                     final KeyedInstanceIdentifier<Tables, TablesKey> tablePath, final Tables newTable) {
408         final Routes routes = newTable.getRoutes();
409         if (routes != null) {
410             final Class<? extends AfiSafiType> afiSafiType = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey())
411                     .get();
412             for (R route : ribSupport.extractAdjRibInRoutes(routes)) {
413                 writeRoutes(tx, tableKey, afiSafiType, ribSupport, tablePath, route.key(), route, false);
414             }
415         }
416     }
417
418     private KeyedInstanceIdentifier<Tables, TablesKey> tablePath(TablesKey tableKey) {
419         return this.effRibTables.child(Tables.class, tableKey);
420     }
421
422     @Override
423     public synchronized void close() {
424         if (this.reg != null) {
425             this.reg.close();
426             this.reg = null;
427         }
428         if (this.submitted != null) {
429             try {
430                 this.submitted.get();
431             } catch (final InterruptedException | ExecutionException throwable) {
432                 LOG.error("Write routes failed", throwable);
433             }
434         }
435         if (this.chain != null) {
436             this.chain.close();
437             this.chain = null;
438         }
439         this.prefixesReceived.values().forEach(LongAdder::reset);
440         this.prefixesInstalled.values().forEach(LongAdder::reset);
441     }
442
443     @Override
444     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
445         final LongAdder counter = this.prefixesReceived.get(tablesKey);
446         if (counter == null) {
447             return 0;
448         }
449         return counter.longValue();
450     }
451
452     @Override
453     public Set<TablesKey> getTableKeys() {
454         return ImmutableSet.copyOf(this.prefixesReceived.keySet());
455     }
456
457     @Override
458     public boolean isSupported(final TablesKey tablesKey) {
459         return this.prefixesReceived.containsKey(tablesKey);
460     }
461
462     @Override
463     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
464         final LongAdder counter = this.prefixesInstalled.get(tablesKey);
465         if (counter == null) {
466             return 0;
467         }
468         return counter.longValue();
469     }
470
471     @Override
472     public long getTotalPrefixesInstalled() {
473         return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
474     }
475
476     private void deleteRT(final Route route) {
477         final Optional<RouteTarget> rtMembership = RouteTargetMembeshipUtil.getRT(route);
478         if (rtMembership.isPresent()) {
479             if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
480                 this.rtCache.uncacheRoute(route);
481             }
482             this.rtMemberships.remove(rtMembership.get());
483             this.rtMembershipsUpdated = true;
484         }
485     }
486 }