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