From: Claudio D. Gasparini Date: Sat, 22 Dec 2018 23:35:40 +0000 (+0100) Subject: Move EffectiveRibInWriter to Binding independent X-Git-Tag: release/neon~35 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=6a3dc48e170fb77ce0bcb27918a1ca548abba4cc;p=bgpcep.git Move EffectiveRibInWriter to Binding independent JIRA: BGPCEP-853 Change-Id: I96e4b858bf41a2c1fb44810c684dd1c9f1d27907 Signed-off-by: Claudio D. Gasparini --- diff --git a/bgp/extensions/route-target/src/main/java/org/opendaylight/protocol/bgp/route/targetcontrain/spi/RouteTargetMembeshipUtil.java b/bgp/extensions/route-target/src/main/java/org/opendaylight/protocol/bgp/route/targetcontrain/spi/RouteTargetMembeshipUtil.java index aba3f630ec..3527d4a381 100644 --- a/bgp/extensions/route-target/src/main/java/org/opendaylight/protocol/bgp/route/targetcontrain/spi/RouteTargetMembeshipUtil.java +++ b/bgp/extensions/route-target/src/main/java/org/opendaylight/protocol/bgp/route/targetcontrain/spi/RouteTargetMembeshipUtil.java @@ -8,8 +8,6 @@ package org.opendaylight.protocol.bgp.route.targetcontrain.spi; -import java.util.Optional; -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.route.target.constrain.rev180618.route.target.constrain.RouteTargetConstrainChoice; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.route.target.constrain.choice.RouteTargetConstrainAs4ExtendedCommunityCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.route.target.constrain.choice.RouteTargetConstrainDefaultCase; @@ -23,12 +21,8 @@ public final class RouteTargetMembeshipUtil { throw new UnsupportedOperationException(); } - public static Optional getRT(final R route) { - if (!(route instanceof RouteTargetConstrainRoute)) { - return Optional.empty(); - } - - final RouteTargetConstrainChoice rtc = ((RouteTargetConstrainRoute) route).getRouteTargetConstrainChoice(); + public static RouteTarget getRT(final RouteTargetConstrainRoute route) { + final RouteTargetConstrainChoice rtc = route.getRouteTargetConstrainChoice(); RouteTarget rt; if (rtc instanceof RouteTargetConstrainDefaultCase) { rt = ((RouteTargetConstrainDefaultCase) rtc).getRouteTargetConstrainDefaultRoute(); @@ -39,6 +33,6 @@ public final class RouteTargetMembeshipUtil { } else { rt = ((RouteTargetConstrainRouteCase) rtc).getRouteTargetExtendedCommunity(); } - return Optional.of(rt); + return rt; } } diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ApplicationPeer.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ApplicationPeer.java index 3065592637..aa1a83f22c 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ApplicationPeer.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ApplicationPeer.java @@ -157,7 +157,7 @@ public class ApplicationPeer extends AbstractPeer implements ClusteredDOMDataTre this.adjRibInWriter = this.adjRibInWriter.transform(this.peerId, this.peerPath, context, localTables, Collections.emptyMap(), registerAppPeerListener); this.effectiveRibInWriter = new EffectiveRibInWriter(this, this.rib, - this.rib.createPeerChain(this), this.peerIId, localTables, this.tableTypeRegistry, + this.rib.createPeerDOMChain(this), this.peerPath, localTables, this.tableTypeRegistry, new ArrayList<>(), this.rtCache); this.effectiveRibInWriter.init(); this.bgpSessionState.registerMessagesCounter(this); diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.java index 44fdae3396..0318cb6158 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.java @@ -467,10 +467,10 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener { private synchronized void createEffRibInWriter() { this.effRibInWriter = new EffectiveRibInWriter(this, this.rib, - this.rib.createPeerChain(this), - this.peerIId, this.tables, this.tableTypeRegistry, - this.rtMemberships, - this.rtCache); + this.rib.createPeerDOMChain(this), + this.peerPath, this.tables, this.tableTypeRegistry, + this.rtMemberships, + this.rtCache); } //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability 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..74ec29e975 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,9 +7,10 @@ */ package org.opendaylight.protocol.bgp.rib.impl; -import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -26,18 +27,17 @@ 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.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.mdsal.common.api.CommitInfo; 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; @@ -51,30 +51,28 @@ import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009 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.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.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.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.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; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +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.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,7 +91,7 @@ import org.slf4j.LoggerFactory; */ @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); @@ -109,14 +107,14 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn .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 +127,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 +136,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(EffectiveRibIn.QNAME); 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 +149,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.QNAME).node(Tables.QNAME)); 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,17 +162,39 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn } @Override - public synchronized void onDataTreeChanged(@Nonnull final Collection> changes) { + public synchronized void onDataTreeChanged(@Nonnull 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); + DOMDataWriteTransaction 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); + } } + 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 if (this.rtMembershipsUpdated) { this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId()); @@ -184,153 +204,232 @@ 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) { + private void changeDataTree( + final DOMDataWriteTransaction tx, + final YangInstanceIdentifier rootPath, + final DataTreeCandidateNode root, + final DataTreeCandidateNode table) { + final PathArgument lastArg = table.getIdentifier(); + Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, + "Unexpected type %s in path %s", lastArg.getClass(), rootPath); + final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg; + final ModificationType modificationType = root.getModificationType(); + final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey); + final TablesKey tk = ribSupport.getTablesKey(); + final YangInstanceIdentifier effectiveTablePath = effectiveTablePath(tableKey); + + switch (modificationType) { + case DISAPPEARED: + case DELETE: + processTableChildren(tx, ribSupport, effectiveTablePath, table.getChildNodes()); + LOG.debug("Delete Effective Table {} modification type {}, ", effectiveTablePath, modificationType); + tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath); + break; + case APPEARED: + case WRITE: + final RIBSupportContext ctx = this.registry.getRIBSupportContext(tk); + LOG.trace("Create Empty table {}", tk); + ctx.createEmptyTableStructure(tx, effectiveTablePath); + processTableChildren(tx, ribSupport, effectiveTablePath, table.getChildNodes()); + break; + case SUBTREE_MODIFIED: + processTableChildren(tx, ribSupport, effectiveTablePath, table.getChildNodes()); + break; + case UNMODIFIED: + LOG.info("Ignoring spurious notification on {} data {}", rootPath, table); + break; + default: + LOG.warn("Ignoring unhandled root {}", table); + break; + } + } + + private void processTableChildren( + final DOMDataWriteTransaction tx, + final RIBSupport ribSupport, + final YangInstanceIdentifier effectiveTablePath, + final Collection children) { + for (final DataTreeCandidateNode child : children) { + final PathArgument childIdentifier = child.getIdentifier(); + final Optional> childDataAfter = child.getDataAfter(); + LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}", childIdentifier, child + .getModificationType(), childDataAfter, child.getDataBefore()); + final YangInstanceIdentifier routesPath = effectiveTablePath.node(childIdentifier); + switch (child.getModificationType()) { 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); + case DISAPPEARED: + processDeleteRouteTables(child, childIdentifier, ribSupport, routesPath); + tx.delete(LogicalDatastoreType.OPERATIONAL, routesPath); + LOG.debug("Route deleted. routeId={}", routesPath); + break; + case UNMODIFIED: + // No-op 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; - } - - 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()); - } - - final DataObjectModification routesChangesContainer = table.getModifiedChildContainer( - ribSupport.routesCaseClass(), ribSupport.routesContainerClass()); - - if (routesChangesContainer == null) { - break; - } - updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren()); + processModifiedRouteTables(child, childIdentifier,tx, ribSupport, routesPath, childDataAfter); break; + case APPEARED: case WRITE: - writeTable(tx, table); + writeRouteTables(child, childIdentifier, tx, ribSupport, routesPath, childDataAfter); break; default: - LOG.warn("Ignoring unhandled root {}", table); + LOG.warn("Ignoring unhandled child {}", child); break; } } + } - 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); + private void processDeleteRouteTables( + final DataTreeCandidateNode child, + final PathArgument childIdentifier, + final RIBSupport ribSupport, + final YangInstanceIdentifier routesPath) { + if (TABLE_ROUTES.equals(childIdentifier)) { + final Collection changedRoutes = ribSupport.changedRoutes(child); + for (final DataTreeCandidateNode route : changedRoutes) { + handleRouteTarget(ModificationType.DELETE, ribSupport, routesPath.getParent(), + route.getDataBefore().orElse(null)); + final TablesKey tablesKey = ribSupport.getTablesKey(); + CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey); } - }, MoreExecutors.directExecutor()); + } } - @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) { - - 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(); - - switch (routeChanged.getModificationType()) { - case SUBTREE_MODIFIED: - case WRITE: - if (afiSafiType == null) { - afiSafiType = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get(); - } - - 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 processModifiedRouteTables( + final DataTreeCandidateNode child, + final PathArgument childIdentifier, + final DOMDataWriteTransaction tx, + final RIBSupport ribSupport, + final YangInstanceIdentifier routesPath, + final Optional> childDataAfter) { + if (TABLE_ROUTES.equals(childIdentifier)) { + final Collection changedRoutes = ribSupport.changedRoutes(child); + for (final DataTreeCandidateNode route : changedRoutes) { + processRoute(tx, ribSupport, routesPath.getParent(), route); } + } else { + tx.put(LogicalDatastoreType.OPERATIONAL, routesPath, childDataAfter.get()); } } - 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); - - final Attributes routeAttrs = route.getAttributes(); - 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)) { - deleteRoutes(routeIID, route, tx); - return; + private void writeRouteTables( + final DataTreeCandidateNode child, + final PathArgument childIdentifier, + final DOMDataWriteTransaction tx, + final RIBSupport ribSupport, + final YangInstanceIdentifier routesPath, + final Optional> childDataAfter) { + if (TABLE_ROUTES.equals(childIdentifier)) { + final Collection changedRoutes = ribSupport.changedRoutes(child); + if (!changedRoutes.isEmpty()) { + tx.put(LogicalDatastoreType.OPERATIONAL, routesPath, childDataAfter.get()); + // Routes are special, as they may end up being filtered. The previous put conveniently + // ensured that we have them in at target, so a subsequent delete will not fail :) + for (final DataTreeCandidateNode route : changedRoutes) { + processRoute(tx, ribSupport, routesPath.getParent(), route); + } } - optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs)); - } else { - optEffAtt = this.ribPolicies.applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType); } - if (!optEffAtt.isPresent()) { - deleteRoutes(routeIID, route, tx); - return; + } + + private void processRoute( + final DOMDataWriteTransaction tx, + final RIBSupport ribSupport, + final YangInstanceIdentifier routesPath, + final DataTreeCandidateNode route) { + LOG.debug("Process route {}", route.getIdentifier()); + final YangInstanceIdentifier routePath = ribSupport.routePath(routesPath, route.getIdentifier()); + final TablesKey tablesKey = ribSupport.getTablesKey(); + switch (route.getModificationType()) { + case DELETE: + case DISAPPEARED: + deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey); + break; + case UNMODIFIED: + // No-op + break; + case APPEARED: + case SUBTREE_MODIFIED: + case WRITE: + CountersUtil.increment(this.prefixesReceived.get(tablesKey), tablesKey); + // Lookup per-table attributes from RIBSupport + final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), + 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. + final boolean longLivedStale = false; + 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, route.getDataBefore().orElse(null), tablesKey); + return; + } + optEffAtt = Optional.of(wrapLongLivedStale(routeAttrs)); + } else { + final Class afiSafiType + = tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get(); + optEffAtt = this.ribPolicies + .applyImportPolicies(this.peerImportParameters, routeAttrs, afiSafiType); + } + if (!optEffAtt.isPresent()) { + deleteRoute(tx, ribSupport, routePath, route.getDataBefore().orElse(null), tablesKey); + return; + } + final NormalizedNode routeChanged = route.getDataAfter().get(); + handleRouteTarget(ModificationType.WRITE, ribSupport, routePath, routeChanged); + tx.put(LogicalDatastoreType.OPERATIONAL, routePath, routeChanged); + CountersUtil.increment(this.prefixesInstalled.get(tablesKey), tablesKey); + + final YangInstanceIdentifier attPath = routePath.node(ribSupport.routeAttributesIdentifier()); + final ContainerNode finalAttribute = ribSupport.attributeToContainerNode(attPath, optEffAtt.get()); + tx.put(LogicalDatastoreType.OPERATIONAL, attPath, finalAttribute); + break; + default: + LOG.warn("Ignoring unhandled route {}", route); + break; } + } + + private void deleteRoute( + final DOMDataWriteTransaction tx, + final RIBSupport ribSupport, + final YangInstanceIdentifier routeIdPath, + final NormalizedNode route, + final TablesKey tablesKey) { + handleRouteTarget(ModificationType.DELETE, ribSupport, routeIdPath, route); + tx.delete(LogicalDatastoreType.OPERATIONAL, routeIdPath); + LOG.debug("Route deleted. routeId={}", routeIdPath); + CountersUtil.decrement(this.prefixesInstalled.get(tablesKey), tablesKey); + } - 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 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); + final RouteTarget rtMembership = RouteTargetMembeshipUtil.getRT(rtc); + if (ModificationType.DELETE == modificationType) { + if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) { + this.rtCache.uncacheRoute(rtc); + } + this.rtMemberships.remove(rtMembership); + } else { + if (PeerRole.Ebgp != this.peerImportParameters.getFromPeerRole()) { + this.rtCache.cacheRoute(rtc); + } + this.rtMemberships.add(rtMembership); } - 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; @@ -351,72 +450,8 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn .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; - } - - writeTableRoutes(tx, tableKey, ribSupport, tablePath, newTable); - } - - 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 KeyedInstanceIdentifier tablePath(TablesKey tableKey) { - return this.effRibTables.child(Tables.class, tableKey); + private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates tableKey) { + return this.effRibTables.node(Tables.QNAME).node(tableKey); } @Override @@ -472,15 +507,4 @@ final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesIn public long getTotalPrefixesInstalled() { return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum(); } - - 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; - } - } } diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/RIBSupportContextRegistry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/RIBSupportContextRegistry.java index cb1d8e9597..d8348a6697 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/RIBSupportContextRegistry.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/RIBSupportContextRegistry.java @@ -49,4 +49,18 @@ public interface RIBSupportContextRegistry { */ @Nullable RIBSupportContext getRIBSupportContext(NodeIdentifierWithPredicates key); + + /** + * Acquire a RIB Support Context for a AFI/SAFI combination. + * + * @param key Tables key with AFI/SAFI key + * @return RIBSupport instance, or null if the AFI/SAFI is not implemented. + */ + @Nullable + default , S extends ChildOf, + R extends Route & ChildOf & Identifiable, + I extends Identifier> RIBSupport getRIBSupport(NodeIdentifierWithPredicates key) { + final RIBSupportContext support = getRIBSupportContext(key); + return support == null ? null : support.getRibSupport(); + } } diff --git a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SynchronizationAndExceptionTest.java b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SynchronizationAndExceptionTest.java index 69255c5801..f4682dc3a1 100644 --- a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SynchronizationAndExceptionTest.java +++ b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SynchronizationAndExceptionTest.java @@ -190,6 +190,7 @@ public class SynchronizationAndExceptionTest extends AbstractAddPathTest { final ListenerRegistration listener = mock(ListenerRegistration.class); doReturn(listener).when(dOMDataTreeChangeService).registerDataTreeChangeListener(any(), any()); doNothing().when(listener).close(); + doNothing().when(this.domChain).close(); doReturn(Collections.singletonMap(DOMDataTreeChangeService.class, dOMDataTreeChangeService)) .when(this.domBroker).getSupportedExtensions(); diff --git a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/config/AppPeerTest.java b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/config/AppPeerTest.java index 2fffb6203c..d4a0b365b5 100644 --- a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/config/AppPeerTest.java +++ b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/config/AppPeerTest.java @@ -56,8 +56,8 @@ public class AppPeerTest extends AbstractConfig { APP_PEER.restart(this.rib, null, this.peerGroupLoader, this.tableTypeRegistry); APP_PEER.instantiateServiceInstance(); Mockito.verify(this.rib, times(6)).getYangRibId(); - Mockito.verify(this.rib, times(2)).getService(); - Mockito.verify(this.listener).close(); + Mockito.verify(this.rib, times(4)).getService(); + Mockito.verify(this.listener, times(2)).close(); assertTrue(APP_PEER.containsEqualConfiguration(this.neighbor)); assertFalse(APP_PEER.containsEqualConfiguration(new NeighborBuilder() diff --git a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/AbstractRIBSupport.java b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/AbstractRIBSupport.java index 71acdb134b..7e1625f7da 100644 --- a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/AbstractRIBSupport.java +++ b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/AbstractRIBSupport.java @@ -69,6 +69,7 @@ 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.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; @@ -520,4 +521,21 @@ public abstract class AbstractRIBSupport< protected final YangInstanceIdentifier routesYangInstanceIdentifier(final YangInstanceIdentifier routesTablePaths) { return this.routesPath.getUnchecked(routesTablePaths); } + + @Override + public R fromNormalizedNode(final YangInstanceIdentifier routePath, final NormalizedNode normalizedNode) { + return (R) this.mappingService.fromNormalizedNode(routePath, normalizedNode).getValue(); + } + + @Override + public Attributes attributeFromContainerNode(final ContainerNode advertisedAttrs) { + final YangInstanceIdentifier path = this.routeDefaultYii.node(routeAttributesIdentifier()); + return (Attributes) this.mappingService.fromNormalizedNode(path, advertisedAttrs).getValue(); + } + + @Override + public ContainerNode attributeToContainerNode(final YangInstanceIdentifier attPath, final Attributes attributes) { + final InstanceIdentifier iid = this.mappingService.fromYangInstanceIdentifier(attPath); + return (ContainerNode) this.mappingService.toNormalizedNode(iid, attributes).getValue(); + } } diff --git a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java index d38532bf9c..fbe9cadc54 100644 --- a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java +++ b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java @@ -39,6 +39,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; /** @@ -250,6 +251,30 @@ public interface RIBSupport< */ TablesKey getTablesKey(); + /** + * Translates supplied YANG Instance Identifier and NormalizedNode into Binding Route. + * + * @param routerId Binding Instance Identifier + * @param normalizedNode NormalizedNode representing Route + * @return Route + */ + R fromNormalizedNode(YangInstanceIdentifier routerId, NormalizedNode normalizedNode); + + /** + * Translates supplied YANG Instance Identifier and NormalizedNode into Binding data Attribute. + * @param advertisedAttrs NormalizedNode representing attributes + * @return Attribute + */ + Attributes attributeFromContainerNode(ContainerNode advertisedAttrs); + + /** + * Translates supplied Binding Instance Identifier and data into NormalizedNode representation. + * @param routePath Binding Instance Identifier pointing to data + * @param attributes Data object representing Attributes + * @return NormalizedNode representation + */ + ContainerNode attributeToContainerNode(YangInstanceIdentifier routePath, Attributes attributes); + interface ApplyRoute { void apply(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier base, @Nonnull NodeIdentifierWithPredicates routeKey,