X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=bgp%2Frib-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fprotocol%2Fbgp%2Frib%2Fimpl%2FEffectiveRibInWriter.java;h=993b34f6938e3bbabb38eb5893491d7e3bd3dbf8;hb=0fdeddbe3d072a88428599421191f0f60b2864e4;hp=5da943c46bc1275d116a71a45b88ecf11dacf814;hpb=6eac726211a8c118b5067093a4d2b7c3b144bba0;p=bgpcep.git
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
index 5da943c46b..993b34f693 100644
--- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
+++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
@@ -7,315 +7,627 @@
*/
package org.opendaylight.protocol.bgp.rib.impl;
+import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
+import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBIN_ATTRIBUTES_AID;
+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 javax.annotation.Nonnull;
-import javax.annotation.concurrent.NotThreadSafe;
-import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
-import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
-import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.path.attributes.Attributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.Route;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.Peer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.PeerKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.AdjRibIn;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.bgp.rib.rib.peer.EffectiveRibIn;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.Tables;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.tables.Routes;
+import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
+import org.opendaylight.protocol.bgp.route.targetcontrain.spi.RouteTargetMembeshipUtil;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.AfiSafiType;
+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.ListenerRegistration;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.Identifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
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.
*/
-@NotThreadSafe
-final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters, AutoCloseable {
- static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
+final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
+ AutoCloseable, ClusteredDOMDataTreeChangeListener {
+
private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
- private final AdjInTracker adjInTracker;
+ private static final TablesKey IVP4_VPN_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
+ MplsLabeledVpnSubsequentAddressFamily.class);
+ private static final TablesKey IVP6_VPN_TABLE_KEY = new TablesKey(Ipv6AddressFamily.class,
+ MplsLabeledVpnSubsequentAddressFamily.class);
+ 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 = Builders.choiceBuilder().withNodeIdentifier(ROUTES_NID).build();
+
+ private final RIBSupportContextRegistry registry;
+ private final YangInstanceIdentifier peerIId;
+ private final YangInstanceIdentifier effRibTables;
+ private final DOMDataTreeChangeService service;
+ private final List rtMemberships;
+ private final RibOutRefresh vpnTableRefresher;
+ private final ClientRouteTargetContrainCache rtCache;
+ private ListenerRegistration> 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) {
+ this.registry = requireNonNull(rib.getRibSupportContext());
+ this.chain = requireNonNull(chain);
+ this.peerIId = requireNonNull(peerIId);
+ this.effRibTables = this.peerIId.node(EFFRIBIN_NID);
+ this.prefixesInstalled = buildPrefixesTables(tables);
+ this.prefixesReceived = buildPrefixesTables(tables);
+ this.ribPolicies = requireNonNull(rib.getRibPolicies());
+ this.service = requireNonNull(rib.getService());
+ this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
+ this.peerImportParameters = peer;
+ this.rtMemberships = rtMemberships;
+ this.rtCache = rtCache;
+ this.vpnTableRefresher = rib;
+ }
- private EffectiveRibInWriter(final BGPRouteEntryImportParameters peer, final RIB rib,
- final BindingTransactionChain chain,
- final KeyedInstanceIdentifier peerIId, final Set tables) {
- this.adjInTracker = new AdjInTracker(peer, rib, chain, peerIId, tables);
+ public void init() {
+ final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
+ this.peerIId.node(ADJRIBIN_NID).node(TABLES_NID));
+ LOG.debug("Registered Effective RIB on {}", this.peerIId);
+ this.reg = requireNonNull(this.service).registerDataTreeChangeListener(treeId, this);
}
- static EffectiveRibInWriter create(final BGPRouteEntryImportParameters peer, @Nonnull final RIB rib,
- @Nonnull final BindingTransactionChain chain,
- @Nonnull final KeyedInstanceIdentifier peerIId,
- @Nonnull final Set tables) {
- return new EffectiveRibInWriter(peer, rib, chain, peerIId, tables);
+ 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 void close() {
- this.adjInTracker.close();
+ public synchronized void onDataTreeChanged(final Collection changes) {
+ if (this.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.getChildNodes()) {
+ if (tx == null) {
+ tx = this.chain.newWriteOnlyTransaction();
+ }
+ changeDataTree(tx, rootPath, root, table);
+ }
+ }
+
+ if (tx != null) {
+ final FluentFuture extends CommitInfo> future = tx.commit();
+ this.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 (this.rtMembershipsUpdated) {
+ this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
+ this.vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
+ this.rtMembershipsUpdated = false;
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ if (this.reg != null) {
+ this.reg.close();
+ this.reg = null;
+ }
+ if (this.submitted != null) {
+ try {
+ this.submitted.get();
+ } catch (final InterruptedException | ExecutionException throwable) {
+ LOG.error("Write routes failed", throwable);
+ }
+ }
+ if (this.chain != null) {
+ this.chain.close();
+ this.chain = null;
+ }
+ this.prefixesReceived.values().forEach(LongAdder::reset);
+ this.prefixesInstalled.values().forEach(LongAdder::reset);
}
@Override
public long getPrefixedReceivedCount(final TablesKey tablesKey) {
- return this.adjInTracker.getPrefixedReceivedCount(tablesKey);
+ final LongAdder counter = this.prefixesReceived.get(tablesKey);
+ if (counter == null) {
+ return 0;
+ }
+ return counter.longValue();
}
@Override
public Set getTableKeys() {
- return this.adjInTracker.getTableKeys();
+ return ImmutableSet.copyOf(this.prefixesReceived.keySet());
}
@Override
public boolean isSupported(final TablesKey tablesKey) {
- return this.adjInTracker.isSupported(tablesKey);
+ return this.prefixesReceived.containsKey(tablesKey);
}
@Override
- public long getPrefixedInstalledCount(@Nonnull final TablesKey tablesKey) {
- return this.adjInTracker.getPrefixedInstalledCount(tablesKey);
+ public long getPrefixedInstalledCount(final TablesKey tablesKey) {
+ final LongAdder counter = this.prefixesInstalled.get(tablesKey);
+ if (counter == null) {
+ return 0;
+ }
+ return counter.longValue();
}
@Override
public long getTotalPrefixesInstalled() {
- return this.adjInTracker.getTotalPrefixesInstalled();
- }
-
- private static final class AdjInTracker implements PrefixesReceivedCounters, PrefixesInstalledCounters,
- AutoCloseable, ClusteredDataTreeChangeListener {
- private final RIBSupportContextRegistry registry;
- private final KeyedInstanceIdentifier peerIId;
- private final InstanceIdentifier effRibTables;
- private final ListenerRegistration> reg;
- private final BindingTransactionChain chain;
- private final Map prefixesReceived;
- private final Map prefixesInstalled;
- private final BGPRibRoutingPolicy ribPolicies;
- private final BGPRouteEntryImportParameters peerImportParameters;
-
- @SuppressWarnings("unchecked")
- AdjInTracker(final BGPRouteEntryImportParameters peer, final RIB rib,
- final BindingTransactionChain chain,
- final KeyedInstanceIdentifier peerIId,
- @Nonnull final Set tables) {
- this.registry = requireNonNull(rib.getRibSupportContext());
- this.chain = requireNonNull(chain);
- this.peerIId = requireNonNull(peerIId);
- this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
- this.prefixesInstalled = buildPrefixesTables(tables);
- this.prefixesReceived = buildPrefixesTables(tables);
- this.ribPolicies = requireNonNull(rib.getRibPolicies());
- this.peerImportParameters = peer;
- final DataTreeIdentifier treeId = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
- this.peerIId.child(AdjRibIn.class).child(Tables.class));
- LOG.debug("Registered Effective RIB on {}", this.peerIId);
- this.reg = requireNonNull(rib.getDataBroker()).registerDataTreeChangeListener(treeId, this);
+ return this.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.getIdentifier();
+ verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(),
+ rootPath);
+ final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
+ final RIBSupportContext ribContext = this.registry.getRIBSupportContext(tableKey);
+ if (ribContext == null) {
+ LOG.warn("Table {} is not supported, ignoring event", tableKey);
+ return;
}
- private Map buildPrefixesTables(final Set tables) {
- final ImmutableMap.Builder b = ImmutableMap.builder();
- tables.forEach(table -> b.put(table, new LongAdder()));
- return b.build();
+ final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey);
+ final ModificationType modificationType = root.getModificationType();
+ 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;
}
+ }
- @Override
- @SuppressWarnings("unchecked")
- public void onDataTreeChanged(@Nonnull final Collection> changes) {
- LOG.trace("Data changed called to effective RIB. Change : {}", changes);
- WriteTransaction tx = null;
- for (final DataTreeModification tc : changes) {
- final DataObjectModification table = tc.getRootNode();
- if (tx == null) {
- tx = this.chain.newWriteOnlyTransaction();
- }
- final DataObjectModification.ModificationType modificationType = table.getModificationType();
- switch (modificationType) {
- case DELETE:
- final Tables removeTable = table.getDataBefore();
- final TablesKey tableKey = removeTable.getKey();
- final KeyedInstanceIdentifier effectiveTablePath
- = this.effRibTables.child(Tables.class, tableKey);
- LOG.debug("Delete Effective Table {} modification type {}, "
- , effectiveTablePath, modificationType);
- tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
- CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
- break;
- case SUBTREE_MODIFIED:
- final Tables before = table.getDataBefore();
- final Tables after = table.getDataAfter();
- final TablesKey tk = after.getKey();
- LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
- tk, modificationType, after, before);
-
- final KeyedInstanceIdentifier tablePath
- = this.effRibTables.child(Tables.class, tk);
- final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
- if (ribSupport == null) {
- break;
- }
- tx.put(LogicalDatastoreType.OPERATIONAL,
- tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
- .rib.rev171207.rib.tables.Attributes.class), after.getAttributes());
-
- final DataObjectModification routesChangesContainer =
- table.getModifiedChildContainer(ribSupport.routesContainerClass());
-
- if (routesChangesContainer == null) {
- return;
- }
- updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
- break;
- case WRITE:
- writeTable(tx, table);
- break;
- default:
- LOG.warn("Ignoring unhandled root {}", table);
- break;
- }
- }
- if (tx != null) {
- tx.submit();
- }
+ 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.getDataBefore());
+ 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.getDataBefore());
+ final boolean longLivedStale = isLongLivedStaleTable(table.getDataAfter());
+ if (wasLongLivedStale != longLivedStale) {
+ LOG.debug("LLGR_STALE flag flipped {}, overwriting table {}", longLivedStale ? "ON" : "OFF",
+ effectiveTablePath);
+ writeTable(tx, ribContext, effectiveTablePath, table);
+ return;
}
- @SuppressWarnings("unchecked")
- private void updateRoutes(
- final WriteTransaction tx,
- final TablesKey tableKey, final RIBSupport ribSupport,
- final KeyedInstanceIdentifier tablePath,
- final Collection> routeChanges) {
- for (final DataObjectModification extends DataObject> routeChanged : routeChanges) {
- final Identifier routeKey
- = ((InstanceIdentifier.IdentifiableItem) routeChanged.getIdentifier()).getKey();
- switch (routeChanged.getModificationType()) {
- case SUBTREE_MODIFIED:
- case WRITE:
- writeRoutes(tx, tableKey, ribSupport, tablePath, routeKey, (Route) routeChanged.getDataAfter());
- break;
- case DELETE:
- final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
- tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
- break;
- }
+ table.getModifiedChild(ATTRIBUTES_NID).ifPresent(modifiedAttrs -> {
+ final YangInstanceIdentifier effAttrsPath = effectiveTablePath.node(ATTRIBUTES_NID);
+ final Optional> optAttrsAfter = modifiedAttrs.getDataAfter();
+ if (optAttrsAfter.isPresent()) {
+ tx.put(LogicalDatastoreType.OPERATIONAL, effAttrsPath, effectiveAttributes(
+ NormalizedNodes.findNode(optAttrsAfter.get(), UPTODATE_NID)));
+ } else {
+ tx.delete(LogicalDatastoreType.OPERATIONAL, effAttrsPath);
}
- }
+ });
- @SuppressWarnings("unchecked")
- private void writeRoutes(final WriteTransaction tx, final TablesKey tk, final RIBSupport ribSupport,
- final KeyedInstanceIdentifier tablePath, final Identifier routeKey,
- final Route route) {
- final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
- CountersUtil.increment(this.prefixesReceived.get(tk), tk);
- final Optional effAtt = this.ribPolicies
- .applyImportPolicies(this.peerImportParameters, route.getAttributes());
- if (effAtt.isPresent()) {
- CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route, true);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), effAtt.get());
+ table.getModifiedChild(ROUTES_NID).ifPresent(modifiedRoutes -> {
+ final RIBSupport, ?, ?, ?> ribSupport = ribContext.getRibSupport();
+ switch (modifiedRoutes.getModificationType()) {
+ 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.getDataAfter(), 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.getDataBefore());
+
+ final Optional> maybeTableAfter = table.getDataAfter();
+ if (maybeTableAfter.isPresent()) {
+ final MapEntryNode tableAfter = extractMapEntry(maybeTableAfter);
+ ribContext.createEmptyTableStructure(tx, effectiveTablePath);
+
+ final Optional> maybeAttrsAfter = tableAfter.getChild(ATTRIBUTES_NID);
+ final boolean longLivedStale;
+ if (maybeAttrsAfter.isPresent()) {
+ final ContainerNode attrsAfter = extractContainer(maybeAttrsAfter);
+ longLivedStale = isLongLivedStale(attrsAfter);
+ tx.put(LogicalDatastoreType.OPERATIONAL, effectiveTablePath.node(ATTRIBUTES_NID),
+ effectiveAttributes(attrsAfter.getChild(UPTODATE_NID)));
} else {
- tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
+ longLivedStale = false;
}
+
+ writeRoutesAfter(tx, ribContext.getRibSupport(), effectiveTablePath,
+ NormalizedNodes.findNode(tableAfter, ROUTES_NID), longLivedStale);
}
+ }
- @SuppressWarnings("unchecked")
- private void writeTable(final WriteTransaction tx, final DataObjectModification table) {
- final Tables newTable = table.getDataAfter();
- if (newTable == null) {
- return;
+ // Performs house-keeping when the contents of a table is deleted
+ private void onDeleteTable(final RIBSupport, ?, ?, ?> ribSupport, final YangInstanceIdentifier effectiveTablePath,
+ final Optional> tableBefore) {
+ // Routes are special in that we need to process the to keep our counters accurate
+ final Optional> maybeRoutesBefore = findRoutesMap(ribSupport,
+ NormalizedNodes.findNode(tableBefore, ROUTES_NID));
+ if (maybeRoutesBefore.isPresent()) {
+ onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).getValue());
+ }
+ }
+
+ private void deleteRoutesBefore(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?, ?, ?> ribSupport,
+ final YangInstanceIdentifier effectiveTablePath, final DataTreeCandidateNode modifiedRoutes) {
+ final Optional> maybeRoutesBefore = NormalizedNodes.findNode(
+ modifiedRoutes.getDataBefore(), ribSupport.relativeRoutesPath());
+ if (maybeRoutesBefore.isPresent()) {
+ onRoutesDeleted(ribSupport, effectiveTablePath, extractMap(maybeRoutesBefore).getValue());
+ }
+ }
+
+ 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).getValue()) {
+ writeRoute(tx, ribSupport, routesPath.node(routeAfter.getIdentifier()), Optional.empty(), routeAfter,
+ longLivedStale);
}
- final TablesKey tableKey = newTable.getKey();
- final KeyedInstanceIdentifier tablePath
- = this.effRibTables.child(Tables.class, tableKey);
-
- // Create an empty table
- LOG.trace("Create Empty table", tablePath);
- tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
- .setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
- .setAttributes(newTable.getAttributes()).build());
-
- final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey);
- final Routes routes = newTable.getRoutes();
- if (ribSupport == null || routes == null) {
- return;
+ }
+ }
+
+ private void onRoutesDeleted(final RIBSupport, ?, ?, ?> ribSupport,
+ final YangInstanceIdentifier effectiveTablePath, final Collection deletedRoutes) {
+ if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
+ final YangInstanceIdentifier routesPath = routeMapPath(ribSupport, effectiveTablePath);
+ for (final MapEntryNode routeBefore : deletedRoutes) {
+ deleteRouteTarget(ribSupport, routesPath.node(routeBefore.getIdentifier()), routeBefore);
}
+ this.rtMembershipsUpdated = true;
+ }
- final DataObjectModification routesChangesContainer =
- table.getModifiedChildContainer(ribSupport.routesContainerClass());
+ 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.getIdentifier());
+ final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier());
+ switch (route.getModificationType()) {
+ case DELETE:
+ case DISAPPEARED:
+ deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null));
+ break;
+ case UNMODIFIED:
+ // No-op
+ break;
+ case APPEARED:
+ case SUBTREE_MODIFIED:
+ case WRITE:
+ writeRoute(tx, ribSupport, routePath, route.getDataBefore(), route.getDataAfter().get(),
+ longLivedStale);
+ break;
+ default:
+ LOG.warn("Ignoring unhandled route {}", route);
+ break;
+ }
+ }
- if (routesChangesContainer == null) {
+ 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(this.prefixesInstalled.get(tablesKey), tablesKey);
+ }
+
+ private void writeRoute(final DOMDataTreeWriteTransaction tx, final RIBSupport, ?, ?, ?> ribSupport,
+ final YangInstanceIdentifier routePath, final Optional> routeBefore,
+ final NormalizedNode, ?> routeAfter, final boolean longLivedStale) {
+ final TablesKey tablesKey = ribSupport.getTablesKey();
+ CountersUtil.increment(this.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.orElse(null));
return;
}
- updateRoutes(tx, tableKey, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
+ optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs));
+ } else {
+ final Class extends AfiSafiType> afiSafiType
+ = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get();
+ optEffAtt = this.ribPolicies
+ .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType);
+ }
+ if (!optEffAtt.isPresent()) {
+ deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null));
+ return;
}
+ handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeAfter);
+ tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeAfter);
+ CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey);
- @Override
- public synchronized void close() {
- this.reg.close();
- this.prefixesReceived.values().forEach(LongAdder::reset);
- this.prefixesInstalled.values().forEach(LongAdder::reset);
+ final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier());
+ final Attributes attToStore = optEffAtt.get();
+ if (!attToStore.equals(routeAttrs)) {
+ final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, attToStore);
+ tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute);
}
+ }
- @Override
- public long getPrefixedReceivedCount(final TablesKey tablesKey) {
- final LongAdder counter = this.prefixesReceived.get(tablesKey);
- if (counter == null) {
- return 0;
- }
- return counter.longValue();
+ private void addRouteTarget(final RouteTargetConstrainRoute rtc) {
+ final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc);
+ if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) {
+ this.rtCache.cacheRoute(rtc);
+ }
+ this.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 != this.peerImportParameters.getFromPeerRole()) {
+ this.rtCache.uncacheRoute(rtc);
}
+ this.rtMemberships.remove(rtMembership);
+ }
- @Override
- public Set getTableKeys() {
- return ImmutableSet.copyOf(this.prefixesReceived.keySet());
+ private void handleRouteTarget(final ModificationType modificationType, final RIBSupport, ?, ?, ?> ribSupport,
+ final YangInstanceIdentifier routeIdPath, final NormalizedNode, ?> route) {
+ if (ribSupport.getSafi() == RouteTargetConstrainSubsequentAddressFamily.class) {
+ final RouteTargetConstrainRoute rtc =
+ (RouteTargetConstrainRoute) ribSupport.fromNormalizedNode(routeIdPath, route);
+ if (ModificationType.DELETE == modificationType) {
+ deleteRouteTarget(rtc);
+ } else {
+ addRouteTarget(rtc);
+ }
+ this.rtMembershipsUpdated = true;
}
+ }
- @Override
- public boolean isSupported(final TablesKey tablesKey) {
- return this.prefixesReceived.containsKey(tablesKey);
+ @SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD")
+ private static Attributes wrapLongLivedStale(final Attributes attrs) {
+ if (attrs == null) {
+ return STALE_LLGR_ATTRIBUTES;
}
- @Override
- public long getPrefixedInstalledCount(final TablesKey tablesKey) {
- final LongAdder counter = this.prefixesInstalled.get(tablesKey);
- if (counter == null) {
- return 0;
+ final List oldCommunities = attrs.getCommunities();
+ final List newCommunities;
+ if (oldCommunities != null) {
+ if (oldCommunities.contains(StaleCommunities.STALE_LLGR)) {
+ return attrs;
}
- return counter.longValue();
+ newCommunities = StaleCommunities.create(oldCommunities);
+ } else {
+ newCommunities = STALE_LLGR_COMMUNUTIES;
}
- @Override
- public long getTotalPrefixesInstalled() {
- return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();
+ 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 this.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 Optional extends NormalizedNode, ?>> optNode) {
+ final NormalizedNode, ?> node = optNode.get();
+ 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.get();
+ verify(node instanceof MapNode, "Expected MapNode, got %s", node);
+ return (MapNode) node;
+ }
+
+ private static MapEntryNode extractMapEntry(final Optional extends NormalizedNode, ?>> optNode) {
+ final NormalizedNode, ?> node = optNode.get();
+ verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node);
+ return (MapEntryNode) node;
+ }
+
+ private static boolean isLongLivedStale(final ContainerNode attributes) {
+ return NormalizedNodes.findNode(attributes, ADJRIBIN_ATTRIBUTES_AID, 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)) : false;
+ }
+
+ private static ContainerNode effectiveAttributes(final Optional extends NormalizedNode, ?>> optUptodate) {
+ return optUptodate.map(leaf -> {
+ final Object value = leaf.getValue();
+ verify(value instanceof Boolean, "Expected boolean uptodate, got %s", value);
+ return ((Boolean) value).booleanValue() ? RIBNormalizedNodes.UPTODATE_ATTRIBUTES
+ : RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES;
+ }).orElse(RIBNormalizedNodes.NOT_UPTODATE_ATTRIBUTES);
}
}