2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.protocol.bgp.rib.impl;
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;
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;
31 import java.util.Optional;
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;
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.
93 * Inbound policy is applied as follows:
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
102 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
103 AutoCloseable, ClusteredDOMDataTreeChangeListener {
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)
116 private static final ChoiceNode EMPTY_ROUTES = Builders.choiceBuilder().withNodeIdentifier(ROUTES_NID).build();
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;
133 private FluentFuture<? extends CommitInfo> submitted;
134 private boolean rtMembershipsUpdated;
136 EffectiveRibInWriter(
137 final BGPRouteEntryImportParameters peer,
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;
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);
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()));
174 public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
175 if (this.chain == null) {
176 LOG.trace("Chain closed. Ignoring Changes : {}", changes);
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()) {
187 tx = this.chain.newWriteOnlyTransaction();
189 changeDataTree(tx, rootPath, root, table);
194 final FluentFuture<? extends CommitInfo> future = tx.commit();
195 this.submitted = future;
196 future.addCallback(new FutureCallback<CommitInfo>() {
198 public void onSuccess(final CommitInfo result) {
199 LOG.trace("Successful commit");
203 public void onFailure(final Throwable trw) {
204 LOG.error("Failed commit", trw);
206 }, MoreExecutors.directExecutor());
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;
218 public synchronized void close() {
219 if (this.reg != null) {
223 if (this.submitted != null) {
225 this.submitted.get();
226 } catch (final InterruptedException | ExecutionException throwable) {
227 LOG.error("Write routes failed", throwable);
230 if (this.chain != null) {
234 this.prefixesReceived.values().forEach(LongAdder::reset);
235 this.prefixesInstalled.values().forEach(LongAdder::reset);
239 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
240 final LongAdder counter = this.prefixesReceived.get(tablesKey);
241 if (counter == null) {
244 return counter.longValue();
248 public Set<TablesKey> getTableKeys() {
249 return ImmutableSet.copyOf(this.prefixesReceived.keySet());
253 public boolean isSupported(final TablesKey tablesKey) {
254 return this.prefixesReceived.containsKey(tablesKey);
258 public long getPrefixedInstalledCount(final TablesKey tablesKey) {
259 final LongAdder counter = this.prefixesInstalled.get(tablesKey);
260 if (counter == null) {
263 return counter.longValue();
267 public long getTotalPrefixesInstalled() {
268 return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
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(),
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);
284 final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
285 final ModificationType modificationType = root.getModificationType();
286 LOG.debug("Effective table {} modification type {}", effectiveTablePath, modificationType);
287 switch (modificationType) {
290 deleteTable(tx, ribContext, effectiveTablePath, table);
294 writeTable(tx, ribContext, effectiveTablePath, table);
296 case SUBTREE_MODIFIED:
297 modifyTable(tx, ribContext, effectiveTablePath, table);
300 LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
303 LOG.warn("Ignoring unhandled root {}", table);
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);
315 private void modifyTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
316 final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
317 LOG.debug("Modify Effective Table {}", effectiveTablePath);
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",
324 writeTable(tx, ribContext, effectiveTablePath, table);
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(optAttrsAfter));
335 tx.delete(LogicalDatastoreType.OPERATIONAL, effAttrsPath);
339 final DataTreeCandidateNode modifiedRoutes = table.getModifiedChild(ROUTES_NID);
340 if (modifiedRoutes != null) {
341 final RIBSupport<?, ?, ?, ?> ribSupport = ribContext.getRibSupport();
342 switch (modifiedRoutes.getModificationType()) {
345 deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
346 // XXX: YANG Tools seems to have an issue stacking DELETE with child WRITE
347 tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID), EMPTY_ROUTES);
348 writeRoutesAfter(tx, ribSupport, effectiveTablePath, modifiedRoutes.getDataAfter(), longLivedStale);
352 deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
353 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID));
355 case SUBTREE_MODIFIED:
356 for (DataTreeCandidateNode modifiedRoute : ribSupport.changedRoutes(modifiedRoutes)) {
357 processRoute(tx, ribSupport, effectiveTablePath, modifiedRoute, longLivedStale);
364 LOG.warn("Ignoring modified routes {}", modifiedRoutes);
370 private void writeTable(final DOMDataWriteTransaction tx, final RIBSupportContext ribContext,
371 final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
372 LOG.debug("Write Effective Table {}", effectiveTablePath);
373 onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.getDataBefore());
375 final Optional<NormalizedNode<?, ?>> maybeTableAfter = table.getDataAfter();
376 if (maybeTableAfter.isPresent()) {
377 final MapEntryNode tableAfter = extractMapEntry(maybeTableAfter);
378 ribContext.createEmptyTableStructure(tx, effectiveTablePath);
380 final Optional<DataContainerChild<?, ?>> maybeAttrsAfter = tableAfter.getChild(ATTRIBUTES_NID);
381 final boolean longLivedStale;
382 if (maybeAttrsAfter.isPresent()) {
383 final ContainerNode attrsAfter = extractContainer(maybeAttrsAfter);
384 longLivedStale = isLongLivedStale(attrsAfter);
385 tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ATTRIBUTES_NID),
386 effectiveAttributes(attrsAfter.getChild(UPTODATE_NID)));
388 longLivedStale = false;
391 writeRoutesAfter(tx, ribContext.getRibSupport(), effectiveTablePath,
392 NormalizedNodes.findNode(tableAfter, ROUTES_NID), longLivedStale);
396 // Performs house-keeping when the contents of a table is deleted
397 private void onDeleteTable(final RIBSupport<?, ?, ?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
398 final Optional<NormalizedNode<?, ?>> tableBefore) {
399 // Routes are special in that we need to process the to keep our counters accurate
400 final Optional<NormalizedNode<?, ?>> maybeRoutesBefore = findRoutesMap(ribSupport,
401 NormalizedNodes.findNode(tableBefore, ROUTES_NID));
402 if (maybeRoutesBefore.isPresent()) {
403 onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).getValue());
407 private void deleteRoutesBefore(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
408 final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode modifiedRoutes) {
409 final Optional<NormalizedNode<?, ?>> maybeRoutesBefore = NormalizedNodes.findNode(
410 modifiedRoutes.getDataBefore(), ribSupport.relativeRoutesPath());
411 if (maybeRoutesBefore.isPresent()) {
412 onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).getValue());
416 private void writeRoutesAfter(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
417 final YangInstanceIdentifier effectiveTablePath, final Optional<NormalizedNode<?, ?>> routesAfter,
418 final boolean longLivedStale) {
419 final Optional<NormalizedNode<?, ?>> maybeRoutesAfter = NormalizedNodes.findNode(routesAfter,
420 ribSupport.relativeRoutesPath());
421 if (maybeRoutesAfter.isPresent()) {
422 final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
423 for (MapEntryNode routeAfter : extractMap(maybeRoutesAfter).getValue()) {
424 writeRoute(tx, ribSupport, routesPath.node(routeAfter.getIdentifier()), Optional.empty(), routeAfter,
430 private void onRoutesDeleted(final RIBSupport<?, ?, ?, ?> ribSupport,
431 final YangInstanceIdentifier effectiveTablePath, final Collection<MapEntryNode> deletedRoutes) {
432 if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
433 final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
434 for (final MapEntryNode routeBefore : deletedRoutes) {
435 deleteRouteTarget(ribSupport, routesPath.node(routeBefore.getIdentifier()), routeBefore);
437 this.rtMembershipsUpdated = true;
440 final TablesKey tablesKey = ribSupport.getTablesKey();
441 CountersUtil.add(prefixesInstalled.get(tablesKey), tablesKey, -deletedRoutes.size());
444 private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
445 final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route, final boolean longLivedStale) {
446 LOG.debug("Process route {}", route.getIdentifier());
447 final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
448 switch (route.getModificationType()) {
451 deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null));
457 case SUBTREE_MODIFIED:
459 writeRoute(tx, ribSupport, routePath, route.getDataBefore(), route.getDataAfter().get(),
462 LOG.warn("Ignoring unhandled route {}", route);
467 private void deleteRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
468 final YangInstanceIdentifier routeIdPath, final NormalizedNode<?, ?> route) {
469 handleRouteTarget(ModificationType.DELETE, ribSupport, routeIdPath, route);
470 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIdPath);
471 LOG.debug("Route deleted. routeId={}", routeIdPath);
472 final TablesKey tablesKey = ribSupport.getTablesKey();
473 CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey);
476 private void writeRoute(final DOMDataWriteTransaction tx, final RIBSupport<?, ?, ?, ?> ribSupport,
477 final YangInstanceIdentifier routePath, final Optional<NormalizedNode<?, ?>> routeBefore,
478 final NormalizedNode<?, ?> routeAfter, final boolean longLivedStale) {
479 final TablesKey tablesKey = ribSupport.getTablesKey();
480 CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey);
481 // Lookup per-table attributes from RIBSupport
482 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(routeAfter,
483 ribSupport.routeAttributesIdentifier()).orElse(null);
484 final Attributes routeAttrs = ribSupport.attributeFromContainerNode(advertisedAttrs);
485 final Optional<Attributes> optEffAtt;
486 // In case we want to add LLGR_STALE we do not process route through policies since it may be
487 // considered as received with LLGR_STALE from peer which is not true.
488 if (longLivedStale) {
489 // LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
490 final List<Communities> effCommunities = routeAttrs.getCommunities();
491 if (effCommunities != null && effCommunities.contains(CommunityUtil.NO_LLGR)) {
492 deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null));
495 optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
497 final Class<? extends AfiSafiType> afiSafiType
498 = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get();
499 optEffAtt = this.ribPolicies
500 .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType);
502 if (!optEffAtt.isPresent()) {
503 deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null));
506 handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeAfter);
507 tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeAfter);
508 CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
510 final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
511 final Attributes attToStore = optEffAtt.get();
512 if (!attToStore.equals(routeAttrs)) {
513 final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, attToStore);
514 tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
518 private void addRouteTarget(final RouteTargetConstrainRoute rtc) {
519 final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
520 if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
521 this.rtCache.cacheRoute(rtc);
523 this.rtMemberships.add(rtMembership);
526 private void deleteRouteTarget(final RIBSupport<?, ?, ?, ?> ribSupport, final YangInstanceIdentifier routeIdPath,
527 final NormalizedNode<?, ?> route) {
528 deleteRouteTarget((RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route));
531 private void deleteRouteTarget(final RouteTargetConstrainRoute rtc) {
532 final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
533 if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
534 this.rtCache.uncacheRoute(rtc);
536 this.rtMemberships.remove(rtMembership);
539 private void handleRouteTarget(final ModificationType modificationType, final RIBSupport<?, ?, ?, ?> ribSupport,
540 final YangInstanceIdentifier routeIdPath, final NormalizedNode<?, ?> route) {
541 if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
542 final RouteTargetConstrainRoute rtc =
543 (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
544 if (ModificationType.DELETE == modificationType) {
545 deleteRouteTarget(rtc);
549 this.rtMembershipsUpdated = true;
553 @SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD")
554 private static Attributes wrapLongLivedStale(final Attributes attrs) {
556 return STALE_LLGR_ATTRIBUTES;
559 final List<Communities> oldCommunities = attrs.getCommunities();
560 final List<Communities> newCommunities;
561 if (oldCommunities != null) {
562 if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
565 newCommunities = StaleCommunities.create(oldCommunities);
567 newCommunities = STALE_LLGR_COMMUNUTIES;
570 return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329
571 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
574 // XXX: this should be moved to YangInstanceIdentifier at some point
575 private static YangInstanceIdentifier concat(final YangInstanceIdentifier parent, final List<PathArgument> args) {
576 YangInstanceIdentifier ret = parent;
577 for (PathArgument arg : args) {
583 private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
584 return this.effRibTables.node(TABLES_NID).node(tableKey);
587 private static YangInstanceIdentifier routeMapPath(final RIBSupport<?, ?, ?, ?> ribSupport,
588 final YangInstanceIdentifier tablePath) {
589 return concat(tablePath.node(ROUTES_NID), ribSupport.relativeRoutesPath());
592 private static Optional<NormalizedNode<?, ?>> findRoutesMap(final RIBSupport<?, ?, ?, ?> ribSupport,
593 final Optional<NormalizedNode<?, ?>> optRoutes) {
594 return NormalizedNodes.findNode(optRoutes, ribSupport.relativeRoutesPath());
597 private static ContainerNode extractContainer(final Optional<? extends NormalizedNode<?, ?>> optNode) {
598 final NormalizedNode<?, ?> node = optNode.get();
599 verify(node instanceof ContainerNode, "Expected ContainerNode, got %s", node);
600 return (ContainerNode) node;
603 private static MapNode extractMap(final Optional<? extends NormalizedNode<?, ?>> optNode) {
604 final NormalizedNode<?, ?> node = optNode.get();
605 verify(node instanceof MapNode, "Expected MapNode, got %s", node);
606 return (MapNode) node;
609 private static MapEntryNode extractMapEntry(final Optional<? extends NormalizedNode<?, ?>> optNode) {
610 final NormalizedNode<?, ?> node = optNode.get();
611 verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node);
612 return (MapEntryNode) node;
615 private static boolean isLongLivedStale(final ContainerNode attributes) {
616 return NormalizedNodes.findNode(attributes, ADJRIBIN_ATTRIBUTES_AID, LLGR_STALE_NID).isPresent();
619 private static boolean isLongLivedStaleTable(final Optional<NormalizedNode<?, ?>> optTable) {
620 final Optional<NormalizedNode<?, ?>> optAttributes = NormalizedNodes.findNode(optTable, ATTRIBUTES_NID);
621 return optAttributes.isPresent() ? isLongLivedStale(extractContainer(optAttributes)) : false;
624 private static ContainerNode effectiveAttributes(final Optional<? extends NormalizedNode<?, ?>> optUptodate) {
625 return optUptodate.map(leaf -> {
626 final Object value = leaf.getValue();
627 verify(value instanceof Boolean, "Expected boolean uptodate, got %s", value);
628 return ((Boolean) value).booleanValue() ? RIBNormalizedNodes.UPTODATE_ATTRIBUTES
629 : RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES;
630 }).orElse(RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES);