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