org.opendaylight.protocol.bgp.rib.impl.EffectiveRibInWriter Maven / Gradle / Ivy
/*
* Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.protocol.bgp.rib.impl;
import static com.google.common.base.Verify.verify;
import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ATTRIBUTES_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.EFFRIBIN_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.LLGR_STALE_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ROUTES_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.UPTODATE_NID;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.LongAdder;
import org.checkerframework.checker.lock.qual.GuardedBy;
import org.checkerframework.checker.lock.qual.Holding;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
import org.opendaylight.protocol.bgp.rib.spi.RIBNormalizedNodes;
import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
import org.opendaylight.protocol.bgp.route.targetcontrain.spi.RouteTargetMembeshipUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.Communities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.RouteTargetConstrainSubsequentAddressFamily;
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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv6AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.MplsLabeledVpnSubsequentAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteTarget;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
* routes in the context of the advertising peer's role and applies the inbound policy.
*
*
* Inbound policy is applied as follows:
*
*
* 1) if the peer is an eBGP peer, perform attribute replacement and filtering
* 2) check if a route is admissible based on attributes attached to it, as well as the
* advertising peer's role
* 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
*
*
* This class is NOT thread-safe.
*/
final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
AutoCloseable, DOMDataTreeChangeListener {
private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
private static final TablesKey IVP4_VPN_TABLE_KEY =
new TablesKey(Ipv4AddressFamily.VALUE, MplsLabeledVpnSubsequentAddressFamily.VALUE);
private static final TablesKey IVP6_VPN_TABLE_KEY =
new TablesKey(Ipv6AddressFamily.VALUE, MplsLabeledVpnSubsequentAddressFamily.VALUE);
private static final ImmutableList STALE_LLGR_COMMUNUTIES =
ImmutableList.of(StaleCommunities.STALE_LLGR);
private static final Attributes STALE_LLGR_ATTRIBUTES = new org.opendaylight.yang.gen.v1.urn.opendaylight.params
.xml.ns.yang.bgp.message.rev200120.path.attributes.AttributesBuilder()
.setCommunities(STALE_LLGR_COMMUNUTIES)
.build();
private static final ChoiceNode EMPTY_ROUTES = ImmutableNodes.newChoiceBuilder()
.withNodeIdentifier(ROUTES_NID)
.build();
private final RIBSupportContextRegistry registry;
private final YangInstanceIdentifier peerIId;
private final YangInstanceIdentifier effRibTables;
private final DataTreeChangeExtension service;
private final List rtMemberships;
private final RibOutRefresh vpnTableRefresher;
private final ClientRouteTargetContrainCache rtCache;
private Registration reg;
private DOMTransactionChain chain;
private final Map prefixesReceived;
private final Map prefixesInstalled;
private final BGPRibRoutingPolicy ribPolicies;
private final BGPRouteEntryImportParameters peerImportParameters;
private final BGPTableTypeRegistryConsumer tableTypeRegistry;
@GuardedBy("this")
private FluentFuture extends CommitInfo> submitted;
private boolean rtMembershipsUpdated;
EffectiveRibInWriter(
final BGPRouteEntryImportParameters peer,
final RIB rib,
final DOMTransactionChain chain,
final YangInstanceIdentifier peerIId,
final Set tables,
final BGPTableTypeRegistryConsumer tableTypeRegistry,
final List rtMemberships,
final ClientRouteTargetContrainCache rtCache) {
registry = requireNonNull(rib.getRibSupportContext());
this.chain = requireNonNull(chain);
this.peerIId = requireNonNull(peerIId);
effRibTables = this.peerIId.node(EFFRIBIN_NID);
prefixesInstalled = buildPrefixesTables(tables);
prefixesReceived = buildPrefixesTables(tables);
ribPolicies = requireNonNull(rib.getRibPolicies());
service = requireNonNull(rib.getService());
this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
peerImportParameters = peer;
this.rtMemberships = rtMemberships;
this.rtCache = rtCache;
vpnTableRefresher = rib;
}
public void init() {
final DOMDataTreeIdentifier treeId = DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL,
peerIId.node(ADJRIBIN_NID).node(TABLES_NID));
LOG.debug("Registered Effective RIB on {}", peerIId);
reg = requireNonNull(service).registerTreeChangeListener(treeId, this);
}
private static Map buildPrefixesTables(final Set tables) {
final ImmutableMap.Builder b = ImmutableMap.builder();
tables.forEach(table -> b.put(table, new LongAdder()));
return b.build();
}
@Override
public synchronized void onInitialData() {
// FIXME: update as if root was deleted
}
@Override
public synchronized void onDataTreeChanged(final List changes) {
if (chain == null) {
LOG.trace("Chain closed. Ignoring Changes : {}", changes);
return;
}
LOG.trace("Data changed called to effective RIB. Change : {}", changes);
DOMDataTreeWriteTransaction tx = null;
for (final DataTreeCandidate tc : changes) {
final YangInstanceIdentifier rootPath = tc.getRootPath();
final DataTreeCandidateNode root = tc.getRootNode();
for (final DataTreeCandidateNode table : root.childNodes()) {
if (tx == null) {
tx = chain.newWriteOnlyTransaction();
}
changeDataTree(tx, rootPath, root, table);
}
}
if (tx != null) {
final FluentFuture extends CommitInfo> future = tx.commit();
submitted = future;
future.addCallback(new FutureCallback() {
@Override
public void onSuccess(final CommitInfo result) {
LOG.trace("Successful commit");
}
@Override
public void onFailure(final Throwable trw) {
LOG.error("Failed commit", trw);
}
}, MoreExecutors.directExecutor());
}
//Refresh VPN Table if RT Memberships were updated
if (rtMembershipsUpdated) {
vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, peerImportParameters.getFromPeerId());
vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, peerImportParameters.getFromPeerId());
rtMembershipsUpdated = false;
}
}
@Override
public synchronized void close() {
if (reg != null) {
reg.close();
reg = null;
}
if (submitted != null) {
try {
submitted.get();
} catch (final InterruptedException | ExecutionException throwable) {
LOG.error("Write routes failed", throwable);
}
}
if (chain != null) {
chain.close();
chain = null;
}
prefixesReceived.values().forEach(LongAdder::reset);
prefixesInstalled.values().forEach(LongAdder::reset);
}
@Override
public long getPrefixedReceivedCount(final TablesKey tablesKey) {
final LongAdder counter = prefixesReceived.get(tablesKey);
if (counter == null) {
return 0;
}
return counter.longValue();
}
@Override
public Set getTableKeys() {
return ImmutableSet.copyOf(prefixesReceived.keySet());
}
@Override
public boolean isSupported(final TablesKey tablesKey) {
return prefixesReceived.containsKey(tablesKey);
}
@Override
public long getPrefixedInstalledCount(final TablesKey tablesKey) {
final LongAdder counter = prefixesInstalled.get(tablesKey);
if (counter == null) {
return 0;
}
return counter.longValue();
}
@Override
public long getTotalPrefixesInstalled() {
return prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
}
@Holding("this")
private void changeDataTree(final DOMDataTreeWriteTransaction tx, final YangInstanceIdentifier rootPath,
final DataTreeCandidateNode root, final DataTreeCandidateNode table) {
final PathArgument lastArg = table.name();
verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(),
rootPath);
final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
final RIBSupportContext ribContext = registry.getRIBSupportContext(tableKey);
if (ribContext == null) {
LOG.warn("Table {} is not supported, ignoring event", tableKey);
return;
}
final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
final ModificationType modificationType = root.modificationType();
LOG.debug("Effective table {} modification type {}", effectiveTablePath, modificationType);
switch (modificationType) {
case DISAPPEARED:
case DELETE:
deleteTable(tx, ribContext, effectiveTablePath, table);
break;
case APPEARED:
case WRITE:
writeTable(tx, ribContext, effectiveTablePath, table);
break;
case SUBTREE_MODIFIED:
modifyTable(tx, ribContext, effectiveTablePath, table);
break;
case UNMODIFIED:
LOG.info("Ignoring spurious notification on {} data {}", rootPath, table);
break;
default:
LOG.warn("Ignoring unhandled root {}", table);
break;
}
}
private void deleteTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
LOG.debug("Delete Effective Table {}", effectiveTablePath);
onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.dataBefore());
tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
}
private void modifyTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
LOG.debug("Modify Effective Table {}", effectiveTablePath);
final boolean wasLongLivedStale = isLongLivedStaleTable(table.findDataBefore());
final boolean longLivedStale = isLongLivedStaleTable(table.findDataAfter());
if (wasLongLivedStale != longLivedStale) {
LOG.debug("LLGR_STALE flag flipped {}, overwriting table {}", longLivedStale ? "ON" : "OFF",
effectiveTablePath);
writeTable(tx, ribContext, effectiveTablePath, table);
return;
}
final var modifiedAttrs = table.modifiedChild(ATTRIBUTES_NID);
if (modifiedAttrs != null) {
final YangInstanceIdentifier effAttrsPath = effectiveTablePath.node(ATTRIBUTES_NID);
final var attrsAfter = modifiedAttrs.dataAfter();
if (attrsAfter != null) {
tx.put(LogicalDatastoreType.OPERATIONAL, effAttrsPath,
effectiveAttributes(extractContainer(attrsAfter)));
} else {
tx.delete(LogicalDatastoreType.OPERATIONAL, effAttrsPath);
}
}
final var modifiedRoutes = table.modifiedChild(ROUTES_NID);
if (modifiedRoutes != null) {
final RIBSupport, ?> ribSupport = ribContext.getRibSupport();
switch (modifiedRoutes.modificationType()) {
case APPEARED:
case WRITE:
deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
// XXX: YANG Tools seems to have an issue stacking DELETE with child WRITE
tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID), EMPTY_ROUTES);
writeRoutesAfter(tx, ribSupport, effectiveTablePath, modifiedRoutes.findDataAfter(),
longLivedStale);
break;
case DELETE:
case DISAPPEARED:
deleteRoutesBefore(tx, ribSupport, effectiveTablePath, modifiedRoutes);
tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ROUTES_NID));
break;
case SUBTREE_MODIFIED:
for (DataTreeCandidateNode modifiedRoute : ribSupport.changedRoutes(modifiedRoutes)) {
processRoute(tx, ribSupport, effectiveTablePath, modifiedRoute, longLivedStale);
}
break;
case UNMODIFIED:
// No-op
return;
default:
LOG.warn("Ignoring modified routes {}", modifiedRoutes);
break;
}
}
}
private void writeTable(final DOMDataTreeWriteTransaction tx, final RIBSupportContext ribContext,
final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode table) {
LOG.debug("Write Effective Table {}", effectiveTablePath);
onDeleteTable(ribContext.getRibSupport(), effectiveTablePath, table.dataBefore());
final var node = table.dataAfter();
if (node != null) {
verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node);
final MapEntryNode tableAfter = (MapEntryNode) node;
ribContext.createEmptyTableStructure(tx, effectiveTablePath);
final DataContainerChild maybeAttrsAfter = tableAfter.childByArg(ATTRIBUTES_NID);
final boolean longLivedStale;
if (maybeAttrsAfter != null) {
final ContainerNode attrsAfter = extractContainer(maybeAttrsAfter);
longLivedStale = isLongLivedStale(attrsAfter);
tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ATTRIBUTES_NID),
effectiveAttributes(attrsAfter));
} else {
longLivedStale = false;
}
writeRoutesAfter(tx, ribContext.getRibSupport(), effectiveTablePath,
NormalizedNodes.findNode(tableAfter, ROUTES_NID), longLivedStale);
}
}
// Performs house-keeping when the contents of a table is deleted
private void onDeleteTable(final RIBSupport, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
final @Nullable NormalizedNode tableBefore) {
// Routes are special in that we need to process the to keep our counters accurate
final var maybeRoutesBefore = findRoutesMap(ribSupport, NormalizedNodes.findNode(tableBefore, ROUTES_NID));
if (maybeRoutesBefore.isPresent()) {
onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).body());
}
}
private void deleteRoutesBefore(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode modifiedRoutes) {
final Optional maybeRoutesBefore =
NormalizedNodes.findNode(modifiedRoutes.dataBefore(), ribSupport.relativeRoutesPath());
if (maybeRoutesBefore.isPresent()) {
onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).body());
}
}
private void writeRoutesAfter(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier effectiveTablePath, final Optional routesAfter,
final boolean longLivedStale) {
final Optional maybeRoutesAfter = NormalizedNodes.findNode(routesAfter,
ribSupport.relativeRoutesPath());
if (maybeRoutesAfter.isPresent()) {
final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
for (MapEntryNode routeAfter : extractMap(maybeRoutesAfter).body()) {
writeRoute(tx, ribSupport, routesPath.node(routeAfter.name()), null, routeAfter, longLivedStale);
}
}
}
private void onRoutesDeleted(final RIBSupport, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
final Collection deletedRoutes) {
if (RouteTargetConstrainSubsequentAddressFamily.VALUE.equals(ribSupport.getTablesKey().getSafi())) {
final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
for (final MapEntryNode routeBefore : deletedRoutes) {
deleteRouteTarget(ribSupport, routesPath.node(routeBefore.name()), routeBefore);
}
rtMembershipsUpdated = true;
}
final TablesKey tablesKey = ribSupport.getTablesKey();
CountersUtil.add(prefixesInstalled.get(tablesKey), tablesKey, -deletedRoutes.size());
}
private void processRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route, final boolean longLivedStale) {
LOG.debug("Process route {}", route.name());
final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.name());
switch (route.modificationType()) {
case DELETE:
case DISAPPEARED:
deleteRoute(tx, ribSupport, routePath, route.dataBefore());
break;
case UNMODIFIED:
// No-op
break;
case APPEARED:
case SUBTREE_MODIFIED:
case WRITE:
writeRoute(tx, ribSupport, routePath, route.dataBefore(), route.getDataAfter(), longLivedStale);
break;
default:
LOG.warn("Ignoring unhandled route {}", route);
break;
}
}
private void deleteRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier routeIdPath, final NormalizedNode route) {
handleRouteTarget(ModificationType.DELETE, ribSupport, routeIdPath, route);
tx.delete(LogicalDatastoreType.OPERATIONAL, routeIdPath);
LOG.debug("Route deleted. routeId={}", routeIdPath);
final TablesKey tablesKey = ribSupport.getTablesKey();
CountersUtil.decrement(prefixesInstalled.get(tablesKey), tablesKey);
}
private void writeRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier routePath, final @Nullable NormalizedNode routeBefore,
final @NonNull NormalizedNode routeAfter, final boolean longLivedStale) {
final TablesKey tablesKey = ribSupport.getTablesKey();
CountersUtil.increment(prefixesReceived.get(tablesKey), tablesKey);
// Lookup per-table attributes from RIBSupport
final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(routeAfter,
ribSupport.routeAttributesIdentifier()).orElse(null);
final Attributes routeAttrs = ribSupport.attributeFromContainerNode(advertisedAttrs);
final Optional optEffAtt;
// In case we want to add LLGR_STALE we do not process route through policies since it may be
// considered as received with LLGR_STALE from peer which is not true.
if (longLivedStale) {
// LLGR procedures are in effect. If the route is tagged with NO_LLGR, it needs to be removed.
final List effCommunities = routeAttrs.getCommunities();
if (effCommunities != null && effCommunities.contains(CommunityUtil.NO_LLGR)) {
deleteRoute(tx, ribSupport, routePath, routeBefore);
return;
}
optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
} else {
optEffAtt = ribPolicies.applyImportPolicies(peerImportParameters, routeAttrs,
verifyNotNull(tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey())));
}
if (optEffAtt.isEmpty()) {
deleteRoute(tx, ribSupport, routePath, routeBefore);
return;
}
handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeAfter);
tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeAfter);
CountersUtil.increment(prefixesInstalled.get(tablesKey), tablesKey);
final Attributes attToStore = optEffAtt.orElseThrow();
if (!attToStore.equals(routeAttrs)) {
final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, attToStore);
tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
}
}
private void addRouteTarget(final RouteTargetConstrainRoute rtc) {
final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
if (PeerRole.Ebgp != peerImportParameters.getFromPeerRole()) {
rtCache.cacheRoute(rtc);
}
rtMemberships.add(rtMembership);
}
private void deleteRouteTarget(final RIBSupport, ?> ribSupport, final YangInstanceIdentifier routeIdPath,
final NormalizedNode route) {
deleteRouteTarget((RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route));
}
private void deleteRouteTarget(final RouteTargetConstrainRoute rtc) {
final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
if (PeerRole.Ebgp != peerImportParameters.getFromPeerRole()) {
rtCache.uncacheRoute(rtc);
}
rtMemberships.remove(rtMembership);
}
private void handleRouteTarget(final ModificationType modificationType, final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier routeIdPath, final NormalizedNode route) {
if (RouteTargetConstrainSubsequentAddressFamily.VALUE.equals(ribSupport.getTablesKey().getSafi())) {
final var rtc = (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
if (ModificationType.DELETE == modificationType) {
deleteRouteTarget(rtc);
} else {
addRouteTarget(rtc);
}
rtMembershipsUpdated = true;
}
}
@SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD")
private static Attributes wrapLongLivedStale(final Attributes attrs) {
if (attrs == null) {
return STALE_LLGR_ATTRIBUTES;
}
final List oldCommunities = attrs.getCommunities();
final List newCommunities;
if (oldCommunities != null) {
if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
return attrs;
}
newCommunities = StaleCommunities.create(oldCommunities);
} else {
newCommunities = STALE_LLGR_COMMUNUTIES;
}
return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120
.path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build();
}
// XXX: this should be moved to YangInstanceIdentifier at some point
private static YangInstanceIdentifier concat(final YangInstanceIdentifier parent, final List args) {
YangInstanceIdentifier ret = parent;
for (PathArgument arg : args) {
ret = ret.node(arg);
}
return ret;
}
private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) {
return effRibTables.node(TABLES_NID).node(tableKey);
}
private static YangInstanceIdentifier routeMapPath(final RIBSupport, ?> ribSupport,
final YangInstanceIdentifier tablePath) {
return concat(tablePath.node(ROUTES_NID), ribSupport.relativeRoutesPath());
}
private static Optional findRoutesMap(final RIBSupport, ?> ribSupport,
final Optional optRoutes) {
return NormalizedNodes.findNode(optRoutes, ribSupport.relativeRoutesPath());
}
private static ContainerNode extractContainer(final NormalizedNode node) {
verify(node instanceof ContainerNode, "Expected ContainerNode, got %s", node);
return (ContainerNode) node;
}
private static MapNode extractMap(final Optional extends NormalizedNode> optNode) {
final NormalizedNode node = optNode.orElseThrow();
verify(node instanceof MapNode, "Expected MapNode, got %s", node);
return (MapNode) node;
}
private static boolean isLongLivedStale(final ContainerNode attributes) {
return NormalizedNodes.findNode(attributes, LLGR_STALE_NID).isPresent();
}
private static boolean isLongLivedStaleTable(final Optional optTable) {
final Optional optAttributes = NormalizedNodes.findNode(optTable, ATTRIBUTES_NID);
return optAttributes.isPresent() && isLongLivedStale(extractContainer(optAttributes.orElseThrow()));
}
private static ContainerNode effectiveAttributes(final ContainerNode attrs) {
final var upToDate = attrs.childByArg(UPTODATE_NID);
if (upToDate != null) {
final Object value = upToDate.body();
verify(value instanceof Boolean, "Expected boolean uptodate, got %s", value);
if ((Boolean) value) {
return RIBNormalizedNodes.UPTODATE_ATTRIBUTES;
}
}
return RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES;
}
}