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=68c8c453a452042135268ea65f61a3e50c723b12;hpb=ba349a7396c7674c9a3606bd11c11fed0ec977f4;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 68c8c453a4..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 @@ -9,6 +9,14 @@ 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; @@ -16,6 +24,7 @@ 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; @@ -23,58 +32,55 @@ 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.GuardedBy; -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.DataBroker; -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.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.rev180329.path.attributes.Attributes; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.attributes.Communities; +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.Route; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibIn; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder; 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.rib.rev180329.rib.tables.Routes; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget; +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.ChildOf; -import org.opendaylight.yangtools.yang.binding.ChoiceIn; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.Identifiable; -import org.opendaylight.yangtools.yang.binding.Identifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; -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; @@ -90,13 +96,14 @@ import org.slf4j.LoggerFactory; * 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, ClusteredDataTreeChangeListener { + AutoCloseable, ClusteredDOMDataTreeChangeListener { private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class); - static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME); 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, @@ -104,19 +111,20 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn 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.rev180329.path.attributes.AttributesBuilder() + .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 KeyedInstanceIdentifier peerIId; - private final InstanceIdentifier effRibTables; - private final DataBroker databroker; + 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 BindingTransactionChain chain; + private DOMTransactionChain chain; private final Map prefixesReceived; private final Map prefixesInstalled; private final BGPRibRoutingPolicy ribPolicies; @@ -129,8 +137,8 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn EffectiveRibInWriter( final BGPRouteEntryImportParameters peer, final RIB rib, - final BindingTransactionChain chain, - final KeyedInstanceIdentifier peerIId, + final DOMTransactionChain chain, + final YangInstanceIdentifier peerIId, final Set tables, final BGPTableTypeRegistryConsumer tableTypeRegistry, final List rtMemberships, @@ -138,11 +146,11 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn this.registry = requireNonNull(rib.getRibSupportContext()); this.chain = requireNonNull(chain); this.peerIId = requireNonNull(peerIId); - this.effRibTables = this.peerIId.child(EffectiveRibIn.class); + this.effRibTables = this.peerIId.node(EFFRIBIN_NID); this.prefixesInstalled = buildPrefixesTables(tables); this.prefixesReceived = buildPrefixesTables(tables); this.ribPolicies = requireNonNull(rib.getRibPolicies()); - this.databroker = requireNonNull(rib.getDataBroker()); + this.service = requireNonNull(rib.getService()); this.tableTypeRegistry = requireNonNull(tableTypeRegistry); this.peerImportParameters = peer; this.rtMemberships = rtMemberships; @@ -151,10 +159,10 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn } public void init() { - final DataTreeIdentifier treeId = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, - this.peerIId.child(AdjRibIn.class).child(Tables.class)); + 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.databroker).registerDataTreeChangeListener(treeId, this); + this.reg = requireNonNull(this.service).registerDataTreeChangeListener(treeId, this); } private static Map buildPrefixesTables(final Set tables) { @@ -164,15 +172,39 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn } @Override - public synchronized void onDataTreeChanged(@Nonnull final Collection> changes) { + 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); - if (!changes.isEmpty()) { - processModifications(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 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 @@ -183,121 +215,274 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn } } - @GuardedBy("this") - @SuppressWarnings("unchecked") - private void processModifications(final Collection> changes) { - final WriteTransaction tx = this.chain.newWriteOnlyTransaction(); - for (final DataTreeModification tc : changes) { - final DataObjectModification table = tc.getRootNode(); - final DataObjectModification.ModificationType modificationType = table.getModificationType(); - switch (modificationType) { - case DELETE: - final Tables removeTable = table.getDataBefore(); - final TablesKey tableKey = removeTable.key(); - 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.key(); - 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; - } + @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); + } - final DataObjectModification adjRibAttrsChanged = table.getModifiedChildContainer( - org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329 - .rib.tables.Attributes.class); - if (adjRibAttrsChanged != null) { - tx.put(LogicalDatastoreType.OPERATIONAL, - tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp - .rib.rev180329.rib.tables.Attributes.class), adjRibAttrsChanged.getDataAfter()); - } + @Override + public long getPrefixedReceivedCount(final TablesKey tablesKey) { + final LongAdder counter = this.prefixesReceived.get(tablesKey); + if (counter == null) { + return 0; + } + return counter.longValue(); + } - final DataObjectModification routesChangesContainer = table.getModifiedChildContainer( - ribSupport.routesCaseClass(), ribSupport.routesContainerClass()); + @Override + public Set getTableKeys() { + return ImmutableSet.copyOf(this.prefixesReceived.keySet()); + } - if (routesChangesContainer == null) { - break; - } - updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren()); - break; + @Override + public boolean isSupported(final TablesKey tablesKey) { + return this.prefixesReceived.containsKey(tablesKey); + } + + @Override + 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.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; + } + + 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; + } + } + + 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; + } + + 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); + } + }); + + table.getModifiedChild(ROUTES_NID).ifPresent(modifiedRoutes -> { + final RIBSupport ribSupport = ribContext.getRibSupport(); + switch (modifiedRoutes.getModificationType()) { + case APPEARED: case WRITE: - writeTable(tx, table); + 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 unhandled root {}", table); + LOG.warn("Ignoring modified routes {}", modifiedRoutes); break; } - } + }); + } - final FluentFuture future = tx.commit(); - this.submitted = future; - future.addCallback(new FutureCallback() { - @Override - public void onSuccess(final CommitInfo result) { - LOG.trace("Successful commit"); + 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 { + longLivedStale = false; } - @Override - public void onFailure(final Throwable trw) { - LOG.error("Failed commit", trw); - } - }, MoreExecutors.directExecutor()); + writeRoutesAfter(tx, ribContext.getRibSupport(), effectiveTablePath, + NormalizedNodes.findNode(tableAfter, ROUTES_NID), longLivedStale); + } } - @SuppressWarnings("unchecked") - private , S extends ChildOf, - R extends Route & ChildOf & Identifiable, I extends Identifier> void updateRoutes( - final WriteTransaction tx, - final TablesKey tableKey, final RIBSupport ribSupport, - final KeyedInstanceIdentifier tablePath, - final Collection> routeChanges) { + // 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()); + } + } - Class afiSafiType = null; - for (final DataObjectModification routeChanged : routeChanges) { - final PathArgument routeChangeId = routeChanged.getIdentifier(); - verify(routeChangeId instanceof IdentifiableItem, "Route change %s has invalid identifier %s", - routeChanged, routeChangeId); - final I routeKey = ((IdentifiableItem) routeChangeId).getKey(); + 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()); + } + } - switch (routeChanged.getModificationType()) { - case SUBTREE_MODIFIED: - case WRITE: - if (afiSafiType == null) { - afiSafiType = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get(); - } + 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); + } + } + } - writeRoutes(tx, tableKey, afiSafiType, ribSupport, tablePath, routeKey, - routeChanged.getDataAfter(), false); - break; - case DELETE: - final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey); - deleteRoutes(routeIID, routeChanged.getDataBefore(), tx); - break; + 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 TablesKey tablesKey = ribSupport.getTablesKey(); + CountersUtil.add(prefixesInstalled.get(tablesKey), tablesKey, -deletedRoutes.size()); } - private , S extends ChildOf, - R extends Route & ChildOf & Identifiable, I extends Identifier> void writeRoutes( - final WriteTransaction tx, final TablesKey tk, final Class afiSafiType, - final RIBSupport ribSupport, final KeyedInstanceIdentifier tablePath, - final I routeKey, final R route, final boolean longLivedStale) { - final InstanceIdentifier routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey); - CountersUtil.increment(this.prefixesReceived.get(tk), tk); + 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; + } + } - final Attributes routeAttrs = route.getAttributes(); + 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. @@ -305,32 +490,68 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn // 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)) { - deleteRoutes(routeIID, route, tx); + deleteRoute(tx, ribSupport, routePath, routeBefore.orElse(null)); return; } optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs)); } else { - optEffAtt = this.ribPolicies.applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType); + final Class afiSafiType + = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get(); + optEffAtt = this.ribPolicies + .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType); } if (!optEffAtt.isPresent()) { - deleteRoutes(routeIID, route, tx); + 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); + + 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); + } + } + + 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)); + } - final Optional rtMembership = RouteTargetMembeshipUtil.getRT(route); - if (rtMembership.isPresent()) { - final RouteTarget rt = rtMembership.get(); - if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) { - this.rtCache.cacheRoute(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); + } + + 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.rtMemberships.add(rt); this.rtMembershipsUpdated = true; } - CountersUtil.increment(this.prefixesInstalled.get(tk), tk); - tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route); - tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), optEffAtt.get()); } + @SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD") private static Attributes wrapLongLivedStale(final Attributes attrs) { if (attrs == null) { return STALE_LLGR_ATTRIBUTES; @@ -347,140 +568,66 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn newCommunities = STALE_LLGR_COMMUNUTIES; } - return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329 + return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120 .path.attributes.AttributesBuilder(attrs).setCommunities(newCommunities).build(); } - private void deleteRoutes(final InstanceIdentifier routeIID, - final R route, final WriteTransaction tx) { - deleteRT(route); - tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID); - } - - private , S extends ChildOf, - R extends Route & ChildOf & Identifiable, I extends Identifier> void writeTable( - final WriteTransaction tx, final DataObjectModification table) { - - final Tables newTable = table.getDataAfter(); - if (newTable == null) { - final Tables oldTable = table.getDataBefore(); - if (oldTable != null) { - final TablesKey tableKey = oldTable.key(); - final KeyedInstanceIdentifier tablePath = tablePath(tableKey); - LOG.trace("Delete table at {}", tablePath); - tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath); - - final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey); - if (ribSupport != null) { - final Routes oldRoutes = oldTable.getRoutes(); - if (oldRoutes != null) { - for (R route : ribSupport.extractAdjRibInRoutes(oldRoutes)) { - deleteRT(route); - } - } - } - } - return; - } - - final TablesKey tableKey = newTable.key(); - final KeyedInstanceIdentifier tablePath = tablePath(tableKey); - - // Create an empty table - LOG.trace("Create Empty table at {}", tablePath); - tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder() - .withKey(tableKey).setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi()) - .setAttributes(newTable.getAttributes()).build()); - - final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey); - if (ribSupport == null) { - LOG.trace("No RIB support for {}", tableKey); - return; + // 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); } - - writeTableRoutes(tx, tableKey, ribSupport, tablePath, newTable); + return ret; } - private , S extends ChildOf, - R extends Route & ChildOf & Identifiable, I extends Identifier> void writeTableRoutes( - final WriteTransaction tx, final TablesKey tableKey, final RIBSupport ribSupport, - final KeyedInstanceIdentifier tablePath, final Tables newTable) { - final Routes routes = newTable.getRoutes(); - if (routes != null) { - final Class afiSafiType = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()) - .get(); - for (R route : ribSupport.extractAdjRibInRoutes(routes)) { - writeRoutes(tx, tableKey, afiSafiType, ribSupport, tablePath, route.key(), route, false); - } - } + private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) { + return this.effRibTables.node(TABLES_NID).node(tableKey); } - private KeyedInstanceIdentifier tablePath(TablesKey tableKey) { - return this.effRibTables.child(Tables.class, tableKey); + private static YangInstanceIdentifier routeMapPath(final RIBSupport ribSupport, + final YangInstanceIdentifier tablePath) { + return concat(tablePath.node(ROUTES_NID), ribSupport.relativeRoutesPath()); } - @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); + private static Optional> findRoutesMap(final RIBSupport ribSupport, + final Optional> optRoutes) { + return NormalizedNodes.findNode(optRoutes, ribSupport.relativeRoutesPath()); } - @Override - public long getPrefixedReceivedCount(final TablesKey tablesKey) { - final LongAdder counter = this.prefixesReceived.get(tablesKey); - if (counter == null) { - return 0; - } - return counter.longValue(); + private static ContainerNode extractContainer(final Optional> optNode) { + final NormalizedNode node = optNode.get(); + verify(node instanceof ContainerNode, "Expected ContainerNode, got %s", node); + return (ContainerNode) node; } - @Override - public Set getTableKeys() { - return ImmutableSet.copyOf(this.prefixesReceived.keySet()); + private static MapNode extractMap(final Optional> optNode) { + final NormalizedNode node = optNode.get(); + verify(node instanceof MapNode, "Expected MapNode, got %s", node); + return (MapNode) node; } - @Override - public boolean isSupported(final TablesKey tablesKey) { - return this.prefixesReceived.containsKey(tablesKey); + private static MapEntryNode extractMapEntry(final Optional> optNode) { + final NormalizedNode node = optNode.get(); + verify(node instanceof MapEntryNode, "Expected MapEntryNode, got %s", node); + return (MapEntryNode) node; } - @Override - public long getPrefixedInstalledCount(final TablesKey tablesKey) { - final LongAdder counter = this.prefixesInstalled.get(tablesKey); - if (counter == null) { - return 0; - } - return counter.longValue(); + private static boolean isLongLivedStale(final ContainerNode attributes) { + return NormalizedNodes.findNode(attributes, ADJRIBIN_ATTRIBUTES_AID, LLGR_STALE_NID).isPresent(); } - @Override - public long getTotalPrefixesInstalled() { - return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum(); + private static boolean isLongLivedStaleTable(final Optional> optTable) { + final Optional> optAttributes = NormalizedNodes.findNode(optTable, ATTRIBUTES_NID); + return optAttributes.isPresent() ? isLongLivedStale(extractContainer(optAttributes)) : false; } - private void deleteRT(final Route route) { - final Optional rtMembership = RouteTargetMembeshipUtil.getRT(route); - if (rtMembership.isPresent()) { - if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) { - this.rtCache.uncacheRoute(route); - } - this.rtMemberships.remove(rtMembership.get()); - this.rtMembershipsUpdated = true; - } + private static ContainerNode effectiveAttributes(final Optional> 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); } }