Add onInitialData() callbacks
[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 com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
13 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_ATTRIBUTES_AID;
14 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_NID;
15 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ATTRIBUTES_NID;
16 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.EFFRIBIN_NID;
17 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LLGR_STALE_NID;
18 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ROUTES_NID;
19 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
20 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.UPTODATE_NID;
21
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableMap;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.common.util.concurrent.FluentFuture;
26 import com.google.common.util.concurrent.FutureCallback;
27 import com.google.common.util.concurrent.MoreExecutors;
28 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Set;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.atomic.LongAdder;
36 import org.checkerframework.checker.lock.qual.GuardedBy;
37 import org.checkerframework.checker.lock.qual.Holding;
38 import org.opendaylight.mdsal.common.api.CommitInfo;
39 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
40 import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
45 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
46 import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
50 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
51 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
52 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
53 import org.opendaylight.protocol.bgp.rib.spi.RIBNormalizedNodes;
54 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
55 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
56 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
57 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
58 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.RouteTargetMembeshipUtil;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.Communities;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.RouteTargetConstrainSubsequentAddressFamily;
64 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;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv6AddressFamily;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.MplsLabeledVpnSubsequentAddressFamily;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteTarget;
69 import org.opendaylight.yangtools.concepts.ListenerRegistration;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
73 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
80 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
81 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
82 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
83 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
86
87 /**
88  * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
89  * routes in the context of the advertising peer's role and applies the inbound policy.
90  *
91  * <p>
92  * Inbound policy is applied as follows:
93  *
94  * <p>
95  * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
96  * 2) check if a route is admissible based on attributes attached to it, as well as the
97  * advertising peer's role
98  * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
99  *
100  * <p>
101  * This class is NOT thread-safe.
102  */
103 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
104         AutoCloseable, ClusteredDOMDataTreeChangeListener {
105
106     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
107     private static final TablesKey IVP4_VPN_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
108             MplsLabeledVpnSubsequentAddressFamily.class);
109     private static final TablesKey IVP6_VPN_TABLE_KEY = new TablesKey(Ipv6AddressFamily.class,
110             MplsLabeledVpnSubsequentAddressFamily.class);
111     private static final ImmutableList<Communities> STALE_LLGR_COMMUNUTIES = ImmutableList.of(
112         StaleCommunities.STALE_LLGR);
113     private static final Attributes STALE_LLGR_ATTRIBUTES = new org.opendaylight.yang.gen.v1.urn.opendaylight.params
114             .xml.ns.yang.bgp.message.rev200120.path.attributes.AttributesBuilder()
115             .setCommunities(STALE_LLGR_COMMUNUTIES)
116             .build();
117     private static final ChoiceNode EMPTY_ROUTES = Builders.choiceBuilder().withNodeIdentifier(ROUTES_NID).build();
118
119     private final RIBSupportContextRegistry registry;
120     private final YangInstanceIdentifier peerIId;
121     private final YangInstanceIdentifier effRibTables;
122     private final DOMDataTreeChangeService service;
123     private final List<RouteTarget> rtMemberships;
124     private final RibOutRefresh vpnTableRefresher;
125     private final ClientRouteTargetContrainCache rtCache;
126     private ListenerRegistration<?> reg;
127     private DOMTransactionChain chain;
128     private final Map<TablesKey, LongAdder> prefixesReceived;
129     private final Map<TablesKey, LongAdder> prefixesInstalled;
130     private final BGPRibRoutingPolicy ribPolicies;
131     private final BGPRouteEntryImportParameters peerImportParameters;
132     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
133     @GuardedBy("this")
134     private FluentFuture<? extends CommitInfo> submitted;
135     private boolean rtMembershipsUpdated;
136
137     EffectiveRibInWriter(
138             final BGPRouteEntryImportParameters peer,
139             final RIB rib,
140             final DOMTransactionChain chain,
141             final YangInstanceIdentifier peerIId,
142             final Set<TablesKey> tables,
143             final BGPTableTypeRegistryConsumer tableTypeRegistry,
144             final List<RouteTarget> rtMemberships,
145             final ClientRouteTargetContrainCache rtCache) {
146         this.registry = requireNonNull(rib.getRibSupportContext());
147         this.chain = requireNonNull(chain);
148         this.peerIId = requireNonNull(peerIId);
149         this.effRibTables = this.peerIId.node(EFFRIBIN_NID);
150         this.prefixesInstalled = buildPrefixesTables(tables);
151         this.prefixesReceived = buildPrefixesTables(tables);
152         this.ribPolicies = requireNonNull(rib.getRibPolicies());
153         this.service = requireNonNull(rib.getService());
154         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
155         this.peerImportParameters = peer;
156         this.rtMemberships = rtMemberships;
157         this.rtCache = rtCache;
158         this.vpnTableRefresher = rib;
159     }
160
161     public void init() {
162         final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
163             this.peerIId.node(ADJRIBIN_NID).node(TABLES_NID));
164         LOG.debug("Registered Effective RIB on {}", this.peerIId);
165         this.reg = requireNonNull(this.service).registerDataTreeChangeListener(treeId, this);
166     }
167
168     private static Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
169         final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
170         tables.forEach(table -> b.put(table, new LongAdder()));
171         return b.build();
172     }
173
174     @Override
175     public synchronized void onInitialData() {
176         // FIXME: update as if root was deleted
177     }
178
179     @Override
180     public synchronized void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
181         if (this.chain == null) {
182             LOG.trace("Chain closed. Ignoring Changes : {}", changes);
183             return;
184         }
185
186         LOG.trace("Data changed called to effective RIB. Change : {}", changes);
187         DOMDataTreeWriteTransaction tx = null;
188         for (final DataTreeCandidate tc : changes) {
189             final YangInstanceIdentifier rootPath = tc.getRootPath();
190             final DataTreeCandidateNode root = tc.getRootNode();
191             for (final DataTreeCandidateNode table : root.getChildNodes()) {
192                 if (tx == null) {
193                     tx = this.chain.newWriteOnlyTransaction();
194                 }
195                 changeDataTree(tx, rootPath, root, table);
196             }
197         }
198
199         if (tx != null) {
200             final FluentFuture<? extends CommitInfo> future = tx.commit();
201             this.submitted = future;
202             future.addCallback(new FutureCallback<CommitInfo>() {
203                 @Override
204                 public void onSuccess(final CommitInfo result) {
205                     LOG.trace("Successful commit");
206                 }
207
208                 @Override
209                 public void onFailure(final Throwable trw) {
210                     LOG.error("Failed commit", trw);
211                 }
212             }, MoreExecutors.directExecutor());
213         }
214
215         //Refresh VPN Table if RT Memberships were updated
216         if (this.rtMembershipsUpdated) {
217             this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
218             this.vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
219             this.rtMembershipsUpdated = false;
220         }
221     }
222
223     @Override
224     public synchronized void close() {
225         if (this.reg != null) {
226             this.reg.close();
227             this.reg = null;
228         }
229         if (this.submitted != null) {
230             try {
231                 this.submitted.get();
232             } catch (final InterruptedException | ExecutionException throwable) {
233                 LOG.error("Write routes failed", throwable);
234             }
235         }
236         if (this.chain != null) {
237             this.chain.close();
238             this.chain = null;
239         }
240         this.prefixesReceived.values().forEach(LongAdder::reset);
241         this.prefixesInstalled.values().forEach(LongAdder::reset);
242     }
243
244     @Override
245     public long getPrefixedReceivedCount(final TablesKey tablesKey) {
246         final LongAdder counter = this.prefixesReceived.get(tablesKey);
247         if (counter == null) {
248             return 0;
249         }
250         return counter.longValue();
251     }
252
253     @Override
254     public Set<TablesKey> getTableKeys() {
255         return ImmutableSet.copyOf(this.prefixesReceived.keySet());
256     }
257
258     @Override
259     public boolean isSupported(final TablesKey tablesKey) {
260         return this.prefixesReceived.containsKey(tablesKey);
261     }
262
263     @Override
264     public long getPrefixedInstalledCount(final TablesKey tablesKey) {
265         final LongAdder counter = this.prefixesInstalled.get(tablesKey);
266         if (counter == null) {
267             return 0;
268         }
269         return counter.longValue();
270     }
271
272     @Override
273     public long getTotalPrefixesInstalled() {
274         return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
275     }
276
277     @Holding("this")
278     private void changeDataTree(final DOMDataTreeWriteTransaction tx, final YangInstanceIdentifier rootPath,
279             final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
280         final PathArgument lastArg = table.getIdentifier();
281         verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(),
282             rootPath);
283         final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
284         final RIBSupportContext ribContext = this.registry.getRIBSupportContext(tableKey);
285         if (ribContext == null) {
286             LOG.warn("Table {} is not supported, ignoring event", tableKey);
287             return;
288         }
289
290         final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
291         final ModificationType modificationType = root.getModificationType();
292         LOG.debug("Effective table {} modification type {}", effectiveTablePath, modificationType);
293         switch (modificationType) {
294             case DISAPPEARED:
295             case DELETE:
296                 deleteTable(tx, ribContext, effectiveTablePath, table);
297                 break;
298             case APPEARED:
299             case WRITE:
300                 writeTable(tx, ribContext, effectiveTablePath, table);
301                 break;
302             case SUBTREE_MODIFIED:
303                 modifyTable(tx, ribContext, effectiveTablePath, table);
304                 break;
305             case UNMODIFIED:
306                 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
307                 break;
308             default:
309                 LOG.warn("Ignoring unhandled root {}", table);
310                 break;
311         }
312     }
313
314     private void deleteTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
315             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
316         LOG.debug("Delete Effective Table {}", effectiveTablePath);
317         onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.getDataBefore());
318         tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
319     }
320
321     private void modifyTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
322             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
323         LOG.debug("Modify Effective Table {}", effectiveTablePath);
324
325         final boolean wasLongLivedStale = isLongLivedStaleTable(table.getDataBefore());
326         final boolean longLivedStale = isLongLivedStaleTable(table.getDataAfter());
327         if (wasLongLivedStale != longLivedStale) {
328             LOG.debug("LLGR_STALE flag flipped {}, overwriting table {}", longLivedStale ? "ON" : "OFF",
329                     effectiveTablePath);
330             writeTable(tx, ribContext, effectiveTablePath, table);
331             return;
332         }
333
334         table.getModifiedChild(ATTRIBUTES_NID).ifPresent(modifiedAttrs -> {
335             final YangInstanceIdentifier effAttrsPath = effectiveTablePath.node(ATTRIBUTES_NID);
336             final Optional<NormalizedNode> optAttrsAfter = modifiedAttrs.getDataAfter();
337             if (optAttrsAfter.isPresent()) {
338                 tx.put(LogicalDatastoreType.OPERATIONAL, effAttrsPath, effectiveAttributes(
339                     NormalizedNodes.findNode(optAttrsAfter.get(), UPTODATE_NID)));
340             } else {
341                 tx.delete(LogicalDatastoreType.OPERATIONAL, effAttrsPath);
342             }
343         });
344
345         table.getModifiedChild(ROUTES_NID).ifPresent(modifiedRoutes -> {
346             final RIBSupport<?, ?> ribSupport = ribContext.getRibSupport();
347             switch (modifiedRoutes.getModificationType()) {
348                 case APPEARED:
349                 case WRITE:
350                     deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
351                     // XXX: YANG Tools seems to have an issue stacking DELETE with child WRITE
352                     tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID), EMPTY_ROUTES);
353                     writeRoutesAfter(tx, ribSupport, effectiveTablePath, modifiedRoutes.getDataAfter(), longLivedStale);
354                     break;
355                 case DELETE:
356                 case DISAPPEARED:
357                     deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
358                     tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID));
359                     break;
360                 case SUBTREE_MODIFIED:
361                     for (DataTreeCandidateNode modifiedRoute : ribSupport.changedRoutes(modifiedRoutes)) {
362                         processRoute(tx, ribSupport, effectiveTablePath, modifiedRoute, longLivedStale);
363                     }
364                     break;
365                 case UNMODIFIED:
366                     // No-op
367                     return;
368                 default:
369                     LOG.warn("Ignoring modified routes {}", modifiedRoutes);
370                     break;
371             }
372         });
373     }
374
375     private void writeTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
376             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
377         LOG.debug("Write Effective Table {}", effectiveTablePath);
378         onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.getDataBefore());
379
380         final Optional<NormalizedNode> maybeTableAfter = table.getDataAfter();
381         if (maybeTableAfter.isPresent()) {
382             final MapEntryNode tableAfter = extractMapEntry(maybeTableAfter);
383             ribContext.createEmptyTableStructure(tx, effectiveTablePath);
384
385             final Optional<DataContainerChild> maybeAttrsAfter = tableAfter.findChildByArg(ATTRIBUTES_NID);
386             final boolean longLivedStale;
387             if (maybeAttrsAfter.isPresent()) {
388                 final ContainerNode attrsAfter = extractContainer(maybeAttrsAfter);
389                 longLivedStale = isLongLivedStale(attrsAfter);
390                 tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ATTRIBUTES_NID),
391                     effectiveAttributes(attrsAfter.findChildByArg(UPTODATE_NID)));
392             } else {
393                 longLivedStale = false;
394             }
395
396             writeRoutesAfter(tx, ribContext.getRibSupport(), effectiveTablePath,
397                 NormalizedNodes.findNode(tableAfter, ROUTES_NID), longLivedStale);
398         }
399     }
400
401     // Performs house-keeping when the contents of a table is deleted
402     private void onDeleteTable(final RIBSupport<?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
403             final Optional<NormalizedNode> tableBefore) {
404         // Routes are special in that we need to process the to keep our counters accurate
405         final Optional<NormalizedNode> maybeRoutesBefore = findRoutesMap(ribSupport,
406                 NormalizedNodes.findNode(tableBefore, ROUTES_NID));
407         if (maybeRoutesBefore.isPresent()) {
408             onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).body());
409         }
410     }
411
412     private void deleteRoutesBefore(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
413             final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode modifiedRoutes) {
414         final Optional<NormalizedNode> maybeRoutesBefore = NormalizedNodes.findNode(
415             modifiedRoutes.getDataBefore(), ribSupport.relativeRoutesPath());
416         if (maybeRoutesBefore.isPresent()) {
417             onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).body());
418         }
419     }
420
421     private void writeRoutesAfter(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
422             final YangInstanceIdentifier effectiveTablePath, final Optional<NormalizedNode> routesAfter,
423             final boolean longLivedStale) {
424         final Optional<NormalizedNode> maybeRoutesAfter = NormalizedNodes.findNode(routesAfter,
425             ribSupport.relativeRoutesPath());
426         if (maybeRoutesAfter.isPresent()) {
427             final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
428             for (MapEntryNode routeAfter : extractMap(maybeRoutesAfter).body()) {
429                 writeRoute(tx, ribSupport, routesPath.node(routeAfter.getIdentifier()), Optional.empty(), routeAfter,
430                     longLivedStale);
431             }
432         }
433     }
434
435     private void onRoutesDeleted(final RIBSupport<?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
436             final Collection<MapEntryNode> deletedRoutes) {
437         if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
438             final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
439             for (final MapEntryNode routeBefore : deletedRoutes) {
440                 deleteRouteTarget(ribSupport, routesPath.node(routeBefore.getIdentifier()), routeBefore);
441             }
442             this.rtMembershipsUpdated = true;
443         }
444
445         final TablesKey tablesKey = ribSupport.getTablesKey();
446         CountersUtil.add(prefixesInstalled.get(tablesKey), tablesKey, -deletedRoutes.size());
447     }
448
449     private void processRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
450             final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route, final boolean longLivedStale) {
451         LOG.debug("Process route {}", route.getIdentifier());
452         final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
453         switch (route.getModificationType()) {
454             case DELETE:
455             case DISAPPEARED:
456                 deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null));
457                 break;
458             case UNMODIFIED:
459                 // No-op
460                 break;
461             case APPEARED:
462             case SUBTREE_MODIFIED:
463             case WRITE:
464                 writeRoute(tx, ribSupport, routePath, route.getDataBefore(), route.getDataAfter().get(),
465                     longLivedStale);
466                 break;
467             default:
468                 LOG.warn("Ignoring unhandled route {}", route);
469                 break;
470         }
471     }
472
473     private void deleteRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
474             final YangInstanceIdentifier routeIdPath, final NormalizedNode route) {
475         handleRouteTarget(ModificationType.DELETE, ribSupport, routeIdPath, route);
476         tx.delete(LogicalDatastoreType.OPERATIONAL, routeIdPath);
477         LOG.debug("Route deleted. routeId={}", routeIdPath);
478         final TablesKey tablesKey = ribSupport.getTablesKey();
479         CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
480     }
481
482     private void writeRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport<?, ?> ribSupport,
483             final YangInstanceIdentifier routePath, final Optional<NormalizedNode> routeBefore,
484             final NormalizedNode routeAfter, final boolean longLivedStale) {
485         final TablesKey tablesKey = ribSupport.getTablesKey();
486         CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
487         // Lookup per-table attributes from RIBSupport
488         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(routeAfter,
489             ribSupport.routeAttributesIdentifier()).orElse(null);
490         final Attributes routeAttrs = ribSupport.attributeFromContainerNode(advertisedAttrs);
491         final Optional<Attributes> optEffAtt;
492         // In case we want to add LLGR_STALE we do not process route through policies since it may be
493         // considered as received with LLGR_STALE from peer which is not true.
494         if (longLivedStale) {
495             // LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
496             final List<Communities> effCommunities = routeAttrs.getCommunities();
497             if (effCommunities != null && effCommunities.contains(CommunityUtil.NO_LLGR)) {
498                 deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null));
499                 return;
500             }
501             optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
502         } else {
503             optEffAtt = ribPolicies.applyImportPolicies(peerImportParameters, routeAttrs,
504                 verifyNotNull(tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey())));
505         }
506         if (!optEffAtt.isPresent()) {
507             deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null));
508             return;
509         }
510         handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeAfter);
511         tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeAfter);
512         CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
513
514         final Attributes attToStore = optEffAtt.get();
515         if (!attToStore.equals(routeAttrs)) {
516             final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
517             final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, attToStore);
518             tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
519         }
520     }
521
522     private void addRouteTarget(final RouteTargetConstrainRoute rtc) {
523         final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
524         if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
525             this.rtCache.cacheRoute(rtc);
526         }
527         this.rtMemberships.add(rtMembership);
528     }
529
530     private void deleteRouteTarget(final RIBSupport<?, ?> ribSupport, final YangInstanceIdentifier routeIdPath,
531             final NormalizedNode route) {
532         deleteRouteTarget((RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route));
533     }
534
535     private void deleteRouteTarget(final RouteTargetConstrainRoute rtc) {
536         final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
537         if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
538             this.rtCache.uncacheRoute(rtc);
539         }
540         this.rtMemberships.remove(rtMembership);
541     }
542
543     private void handleRouteTarget(final ModificationType modificationType, final RIBSupport<?, ?> ribSupport,
544             final YangInstanceIdentifier routeIdPath, final NormalizedNode route) {
545         if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
546             final RouteTargetConstrainRoute rtc =
547                 (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
548             if (ModificationType.DELETE == modificationType) {
549                 deleteRouteTarget(rtc);
550             } else {
551                 addRouteTarget(rtc);
552             }
553             this.rtMembershipsUpdated = true;
554         }
555     }
556
557     @SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD")
558     private static Attributes wrapLongLivedStale(final Attributes attrs) {
559         if (attrs == null) {
560             return STALE_LLGR_ATTRIBUTES;
561         }
562
563         final List<Communities> oldCommunities = attrs.getCommunities();
564         final List<Communities> newCommunities;
565         if (oldCommunities != null) {
566             if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
567                 return attrs;
568             }
569             newCommunities = StaleCommunities.create(oldCommunities);
570         } else {
571             newCommunities = STALE_LLGR_COMMUNUTIES;
572         }
573
574         return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120
575                 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
576     }
577
578     // XXX: this should be moved to YangInstanceIdentifier at some point
579     private static YangInstanceIdentifier concat(final YangInstanceIdentifier parent, final List<PathArgument> args) {
580         YangInstanceIdentifier ret = parent;
581         for (PathArgument arg : args) {
582             ret = ret.node(arg);
583         }
584         return ret;
585     }
586
587     private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
588         return this.effRibTables.node(TABLES_NID).node(tableKey);
589     }
590
591     private static YangInstanceIdentifier routeMapPath(final RIBSupport<?, ?> ribSupport,
592             final YangInstanceIdentifier tablePath) {
593         return concat(tablePath.node(ROUTES_NID), ribSupport.relativeRoutesPath());
594     }
595
596     private static Optional<NormalizedNode> findRoutesMap(final RIBSupport<?, ?> ribSupport,
597             final Optional<NormalizedNode> optRoutes) {
598         return NormalizedNodes.findNode(optRoutes, ribSupport.relativeRoutesPath());
599     }
600
601     private static ContainerNode extractContainer(final Optional<? extends NormalizedNode> optNode) {
602         final NormalizedNode node = optNode.get();
603         verify(node instanceof ContainerNode, "Expected ContainerNode, got %s", node);
604         return (ContainerNode) node;
605     }
606
607     private static MapNode extractMap(final Optional<? extends NormalizedNode> optNode) {
608         final NormalizedNode node = optNode.get();
609         verify(node instanceof MapNode, "Expected MapNode, got %s", node);
610         return (MapNode) node;
611     }
612
613     private static MapEntryNode extractMapEntry(final Optional<? extends NormalizedNode> optNode) {
614         final NormalizedNode node = optNode.get();
615         verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node);
616         return (MapEntryNode) node;
617     }
618
619     private static boolean isLongLivedStale(final ContainerNode attributes) {
620         return NormalizedNodes.findNode(attributes, ADJRIBIN_ATTRIBUTES_AID, LLGR_STALE_NID).isPresent();
621     }
622
623     private static boolean isLongLivedStaleTable(final Optional<NormalizedNode> optTable) {
624         final Optional<NormalizedNode> optAttributes = NormalizedNodes.findNode(optTable, ATTRIBUTES_NID);
625         return optAttributes.isPresent() && isLongLivedStale(extractContainer(optAttributes));
626     }
627
628     private static ContainerNode effectiveAttributes(final Optional<? extends NormalizedNode> optUptodate) {
629         return optUptodate.map(leaf -> {
630             final Object value = leaf.body();
631             verify(value instanceof Boolean, "Expected boolean uptodate, got %s", value);
632             return (Boolean) value ? RIBNormalizedNodes.UPTODATE_ATTRIBUTES
633                     : RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES;
634         }).orElse(RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES);
635     }
636 }