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%2FAdjRibInWriter.java;h=f17dbdad5c098ecc823ab847284948c863f0780b;hb=43c2aef773499f76117898c0ce2403c8826449fa;hp=11e96da6914a4fc397ee130dc36a134f8a745299;hpb=a5ac27fe2b5fb6d06fd3100c62510697a64c130d;p=bgpcep.git diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibInWriter.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibInWriter.java index 11e96da691..f17dbdad5c 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibInWriter.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibInWriter.java @@ -7,46 +7,55 @@ */ package org.opendaylight.protocol.bgp.rib.impl; +import static java.util.Objects.requireNonNull; + import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.NotThreadSafe; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; 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.rib.impl.ApplicationPeer.RegisterAppPeerListener; +import org.opendaylight.protocol.bgp.rib.impl.spi.PeerTransactionChain; import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext; import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry; import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils; import org.opendaylight.protocol.bgp.rib.spi.PeerRoleUtil; import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.SendReceive; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlri; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlri; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.SimpleRoutingPolicy; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibIn; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibOut; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.SupportedTables; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId; +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.bgp.rib.rib.Peer; +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.AdjRibOut; +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.bgp.rib.rib.peer.SupportedTables; +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.TablesKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Attributes; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder; @@ -55,6 +64,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; 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.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; @@ -62,18 +73,23 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContaine import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** * Writer of Adjacency-RIB-In for a single peer. An instance of this object * is attached to each {@link BGPPeer} and {@link ApplicationPeer}. */ @NotThreadSafe final class AdjRibInWriter { + private static final Logger LOG = LoggerFactory.getLogger(AdjRibInWriter.class); + @VisibleForTesting - static final LeafNode ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE); - private static final LeafNode ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.getNodeType(), Boolean.TRUE); + static final LeafNode ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, + "uptodate"), Boolean.FALSE); @VisibleForTesting static final QName PEER_ID_QNAME = QName.create(Peer.QNAME, "peer-id").intern(); + private static final LeafNode ATTRIBUTES_UPTODATE_TRUE = + ImmutableNodes.leafNode(ATTRIBUTES_UPTODATE_FALSE.getNodeType(), Boolean.TRUE); private static final QName PEER_ROLE_QNAME = QName.create(Peer.QNAME, "peer-role").intern(); private static final NodeIdentifier ADJRIBIN = new NodeIdentifier(AdjRibIn.QNAME); private static final NodeIdentifier ADJRIBOUT = new NodeIdentifier(AdjRibOut.QNAME); @@ -83,40 +99,41 @@ final class AdjRibInWriter { private static final NodeIdentifier PEER_TABLES = new NodeIdentifier(SupportedTables.QNAME); private static final NodeIdentifier TABLES = new NodeIdentifier(Tables.QNAME); private static final QName SEND_RECEIVE = QName.create(SupportedTables.QNAME, "send-receive").intern(); - private static final NodeIdentifier SIMPLE_ROUTING_POLICY_NID = new NodeIdentifier(QName.create(Peer.QNAME, "simple-routing-policy").intern()); // FIXME: is there a utility method to construct this? - private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build(); - private static final ContainerNode EMPTY_EFFRIBIN = Builders.containerBuilder().withNodeIdentifier(EFFRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build(); - private static final ContainerNode EMPTY_ADJRIBOUT = Builders.containerBuilder().withNodeIdentifier(ADJRIBOUT).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build(); + private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder() + .withNodeIdentifier(ADJRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build(); + private static final ContainerNode EMPTY_EFFRIBIN = Builders.containerBuilder() + .withNodeIdentifier(EFFRIBIN).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build(); + private static final ContainerNode EMPTY_ADJRIBOUT = Builders.containerBuilder() + .withNodeIdentifier(ADJRIBOUT).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build(); private final Map tables; - private final YangInstanceIdentifier peerPath; private final YangInstanceIdentifier ribPath; - private final DOMTransactionChain chain; + private final PeerTransactionChain chain; private final PeerRole role; - private final Optional simpleRoutingPolicy; - - private AdjRibInWriter(final YangInstanceIdentifier ribPath, final DOMTransactionChain chain, final PeerRole role, - final Optional simpleRoutingPolicy, final YangInstanceIdentifier peerPath, final Map tables) { - this.ribPath = Preconditions.checkNotNull(ribPath); - this.chain = Preconditions.checkNotNull(chain); - this.tables = Preconditions.checkNotNull(tables); - this.role = Preconditions.checkNotNull(role); - this.simpleRoutingPolicy = simpleRoutingPolicy; - this.peerPath = peerPath; + @GuardedBy("this") + private final Map> staleRoutesRegistry = new HashMap<>(); + @GuardedBy("this") + private FluentFuture submitted; + + private AdjRibInWriter(final YangInstanceIdentifier ribPath, final PeerTransactionChain chain, final PeerRole role, + final Map tables) { + this.ribPath = requireNonNull(ribPath); + this.chain = requireNonNull(chain); + this.tables = requireNonNull(tables); + this.role = requireNonNull(role); } /** * Create a new writer using a transaction chain. * - * @param role peer's role - * @param simpleRoutingPolicy simple Routing Policy {@link SimpleRoutingPolicy} - *@param chain transaction chain @return A fresh writer instance + * @param role peer's role + * @param chain transaction chain @return A fresh writer instance */ static AdjRibInWriter create(@Nonnull final YangInstanceIdentifier ribId, @Nonnull final PeerRole role, - final Optional simpleRoutingPolicy, @Nonnull final DOMTransactionChain chain) { - return new AdjRibInWriter(ribId, chain, role, simpleRoutingPolicy, null, Collections.emptyMap()); + @Nonnull final PeerTransactionChain chain) { + return new AdjRibInWriter(ribId, chain, role, Collections.emptyMap()); } /** @@ -124,30 +141,33 @@ final class AdjRibInWriter { * Empty tables are created for new entries and old tables are deleted. Once this * method returns, the old instance must not be reasonably used. * - * @param newPeerId new peer BGP identifier - * @param registry RIB extension registry - * @param tableTypes New tables, must not be null + * @param newPeerId new peer BGP identifier + * @param peerPath + * @param registry RIB extension registry + * @param tableTypes New tables, must not be null * @param addPathTablesType * @return New writer */ - AdjRibInWriter transform(final PeerId newPeerId, final RIBSupportContextRegistry registry, - final Set tableTypes, final Map addPathTablesType) { - return transform(newPeerId, registry, tableTypes, addPathTablesType, null); + AdjRibInWriter transform(final PeerId newPeerId, final YangInstanceIdentifier peerPath, + final RIBSupportContextRegistry registry, + final Set tableTypes, final Map addPathTablesType) { + return transform(newPeerId, peerPath, registry, tableTypes, addPathTablesType, null); } - AdjRibInWriter transform(final PeerId newPeerId, final RIBSupportContextRegistry registry, final Set tableTypes, - final Map addPathTablesType, @Nullable final RegisterAppPeerListener registerAppPeerListener) { - final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); + AdjRibInWriter transform(final PeerId newPeerId, final YangInstanceIdentifier peerPath, + final RIBSupportContextRegistry registry, final Set tableTypes, + final Map addPathTablesType, + @Nullable final RegisterAppPeerListener registerAppPeerListener) { + final DOMDataWriteTransaction tx = this.chain.getDomChain().newWriteOnlyTransaction(); - final YangInstanceIdentifier newPeerPath; - newPeerPath = createEmptyPeerStructure(newPeerId, tx); - final ImmutableMap tb = createNewTableInstances(newPeerPath, registry, tableTypes, - addPathTablesType, tx); + createEmptyPeerStructure(newPeerId, peerPath, tx); + final ImmutableMap tb = createNewTableInstances(peerPath, registry, tableTypes, + addPathTablesType, tx); - Futures.addCallback(tx.submit(), new FutureCallback() { + tx.commit().addCallback(new FutureCallback() { @Override - public void onSuccess(final Void result) { - if(registerAppPeerListener != null) { + public void onSuccess(final CommitInfo result) { + if (registerAppPeerListener != null) { LOG.trace("Application Peer Listener registered"); registerAppPeerListener.register(); } @@ -155,29 +175,24 @@ final class AdjRibInWriter { @Override public void onFailure(final Throwable throwable) { - if(registerAppPeerListener != null) { + if (registerAppPeerListener != null) { LOG.error("Failed to create Empty Structure, Application Peer Listener won't be registered", - throwable); + throwable); } else { LOG.error("Failed to create Empty Structure", throwable); } } - }); - return new AdjRibInWriter(this.ribPath, this.chain, this.role, this.simpleRoutingPolicy, newPeerPath, tb); + }, MoreExecutors.directExecutor()); + return new AdjRibInWriter(this.ribPath, this.chain, this.role, tb); } /** * Create new table instances, potentially creating their empty entries - * @param newPeerPath - * @param registry - * @param tableTypes - * @param addPathTablesType - * @param tx - * @return */ - private ImmutableMap createNewTableInstances(final YangInstanceIdentifier newPeerPath, - final RIBSupportContextRegistry registry, final Set tableTypes, final Map addPathTablesType, - final DOMDataWriteTransaction tx) { + private static ImmutableMap createNewTableInstances( + final YangInstanceIdentifier newPeerPath, final RIBSupportContextRegistry registry, + final Set tableTypes, final Map addPathTablesType, + final DOMDataWriteTransaction tx) { final Builder tb = ImmutableMap.builder(); for (final TablesKey tableKey : tableTypes) { @@ -188,50 +203,53 @@ final class AdjRibInWriter { LOG.warn("No support for table type {}, skipping it", tableKey); continue; } - installAdjRibsOutTables(newPeerPath, rs, instanceIdentifierKey, tableKey, addPathTablesType.get(tableKey), tx); + installAdjRibsOutTables(newPeerPath, rs, instanceIdentifierKey, tableKey, + addPathTablesType.get(tableKey), tx); installAdjRibInTables(newPeerPath, tableKey, rs, instanceIdentifierKey, tx, tb); } return tb.build(); } - private void installAdjRibInTables(final YangInstanceIdentifier newPeerPath, final TablesKey tableKey, final RIBSupportContext rs, - final NodeIdentifierWithPredicates instanceIdentifierKey, final DOMDataWriteTransaction tx, final Builder tb) { + private static void installAdjRibInTables(final YangInstanceIdentifier newPeerPath, final TablesKey tableKey, + final RIBSupportContext rs, final NodeIdentifierWithPredicates instanceIdentifierKey, + final DOMDataWriteTransaction tx, final Builder tb) { // We will use table keys very often, make sure they are optimized - final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES)); + final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newPeerPath + .node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES)); idb.nodeWithKey(instanceIdentifierKey.getNodeType(), instanceIdentifierKey.getKeyValues()); final TableContext ctx = new TableContext(rs, idb.build()); ctx.createEmptyTableStructure(tx); - tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE); + tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME) + .node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE); LOG.debug("Created table instance {}", ctx.getTableId()); tb.put(tableKey, ctx); } - private void installAdjRibsOutTables(final YangInstanceIdentifier newPeerPath, final RIBSupportContext rs, - final NodeIdentifierWithPredicates instanceIdentifierKey, final TablesKey tableKey, final SendReceive sendReceive, - final DOMDataWriteTransaction tx) { - if (!isAnnounceNone(this.simpleRoutingPolicy)) { - final NodeIdentifierWithPredicates supTablesKey = RibSupportUtils.toYangKey(SupportedTables.QNAME, tableKey); - final DataContainerNodeAttrBuilder tt = Builders.mapEntryBuilder().withNodeIdentifier(supTablesKey); - for (final Entry e : supTablesKey.getKeyValues().entrySet()) { - tt.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue())); - } - if(sendReceive != null) { - tt.withChild(ImmutableNodes.leafNode(SEND_RECEIVE, sendReceive.toString().toLowerCase())); - } - tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath.node(PEER_TABLES).node(supTablesKey), tt.build()); - rs.createEmptyTableStructure(tx, newPeerPath.node(EMPTY_ADJRIBOUT.getIdentifier()).node(TABLES).node(instanceIdentifierKey)); + private static void installAdjRibsOutTables(final YangInstanceIdentifier newPeerPath, final RIBSupportContext rs, + final NodeIdentifierWithPredicates instanceIdentifierKey, final TablesKey tableKey, + final SendReceive sendReceive, final DOMDataWriteTransaction tx) { + final NodeIdentifierWithPredicates supTablesKey = RibSupportUtils.toYangKey(SupportedTables.QNAME, tableKey); + final DataContainerNodeAttrBuilder tt = + Builders.mapEntryBuilder().withNodeIdentifier(supTablesKey); + for (final Entry e : supTablesKey.getKeyValues().entrySet()) { + tt.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue())); } + if (sendReceive != null) { + tt.withChild(ImmutableNodes.leafNode(SEND_RECEIVE, sendReceive.toString().toLowerCase(Locale.ENGLISH))); + } + tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath.node(PEER_TABLES).node(supTablesKey), tt.build()); + rs.createEmptyTableStructure(tx, newPeerPath.node(EMPTY_ADJRIBOUT.getIdentifier()) + .node(TABLES).node(instanceIdentifierKey)); } - private YangInstanceIdentifier createEmptyPeerStructure(final PeerId newPeerId, final DOMDataWriteTransaction tx) { + private void createEmptyPeerStructure(final PeerId newPeerId, + final YangInstanceIdentifier peerPath, final DOMDataWriteTransaction tx) { final NodeIdentifierWithPredicates peerKey = IdentifierUtils.domPeerId(newPeerId); - final YangInstanceIdentifier newPeerPath = this.ribPath.node(Peer.QNAME).node(peerKey); - tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, peerSkeleton(peerKey, newPeerId.getValue())); - LOG.debug("New peer {} structure installed.", newPeerPath); - return newPeerPath; + tx.put(LogicalDatastoreType.OPERATIONAL, peerPath, peerSkeleton(peerKey, newPeerId.getValue())); + LOG.debug("New peer {} structure installed.", peerPath); } @VisibleForTesting @@ -240,49 +258,33 @@ final class AdjRibInWriter { pb.withNodeIdentifier(peerKey); pb.withChild(ImmutableNodes.leafNode(PEER_ID, peerId)); pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, PeerRoleUtil.roleForString(this.role))); - if (this.simpleRoutingPolicy.isPresent() && this.role != PeerRole.Internal) { - pb.withChild(ImmutableNodes.leafNode(SIMPLE_ROUTING_POLICY_NID, simpleRoutingPolicyString(this.simpleRoutingPolicy.get()))); - } pb.withChild(ImmutableMapNodeBuilder.create().withNodeIdentifier(PEER_TABLES).build()); pb.withChild(EMPTY_ADJRIBIN); - if(!isLearnNone(this.simpleRoutingPolicy)) { - pb.withChild(EMPTY_EFFRIBIN); - } - if (!isAnnounceNone(this.simpleRoutingPolicy)) { - pb.withChild(EMPTY_ADJRIBOUT); - } + pb.withChild(EMPTY_EFFRIBIN); + pb.withChild(EMPTY_ADJRIBOUT); return pb.build(); } - ListenableFuture removePeer() { - if(this.peerPath != null) { - final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); - tx.delete(LogicalDatastoreType.OPERATIONAL, this.peerPath); - final CheckedFuture future = tx.submit(); - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(final Void result) { - LOG.debug("Peer {} removed", AdjRibInWriter.this.peerPath); - } - - @Override - public void onFailure(final Throwable t) { - LOG.warn("Failed to remove Peer {}", AdjRibInWriter.this.peerPath, t); - } - }); - return future; - } - return Futures.immediateFuture(null); - } - void markTableUptodate(final TablesKey tableTypes) { - final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); + final DOMDataWriteTransaction tx = this.chain.getDomChain().newWriteOnlyTransaction(); final TableContext ctx = this.tables.get(tableTypes); - tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE); - tx.submit(); + tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME) + .node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE); + tx.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.trace("Write Attributes uptodate, succeed"); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Write Attributes uptodate failed", throwable); + } + }, MoreExecutors.directExecutor()); } - void updateRoutes(final MpReachNlri nlri, final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes attributes) { + void updateRoutes(final MpReachNlri nlri, final org.opendaylight.yang.gen.v1.urn.opendaylight.params + .xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes attributes) { final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi()); final TableContext ctx = this.tables.get(key); if (ctx == null) { @@ -290,10 +292,26 @@ final class AdjRibInWriter { return; } - final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); - ctx.writeRoutes(tx, nlri, attributes); + final DOMDataWriteTransaction tx = this.chain.getDomChain().newWriteOnlyTransaction(); + final Collection routeKeys = ctx.writeRoutes(tx, nlri, attributes); + final Collection staleRoutes = this.staleRoutesRegistry.get(key); + if (staleRoutes != null) { + staleRoutes.removeAll(routeKeys); + } LOG.trace("Write routes {}", nlri); - tx.submit(); + final FluentFuture future = tx.commit(); + this.submitted = future; + future.addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.trace("Write routes {}, succeed", nlri); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Write routes failed", throwable); + } + }, MoreExecutors.directExecutor()); } void removeRoutes(final MpUnreachNlri nlri) { @@ -304,27 +322,127 @@ final class AdjRibInWriter { return; } LOG.trace("Removing routes {}", nlri); - final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); + final DOMDataWriteTransaction tx = this.chain.getDomChain().newWriteOnlyTransaction(); ctx.removeRoutes(tx, nlri); - tx.submit(); + final FluentFuture future = tx.commit(); + this.submitted = future; + future.addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.trace("Removing routes {}, succeed", nlri); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Removing routes failed", throwable); + } + }, MoreExecutors.directExecutor()); + } + + void releaseChain() { + if (this.submitted != null) { + try { + this.submitted.get(); + } catch (final InterruptedException | ExecutionException throwable) { + LOG.error("Write routes failed", throwable); + } + } } - static boolean isAnnounceNone(final java.util.Optional peerStatus) { - return peerStatus.isPresent() && peerStatus.get() == SimpleRoutingPolicy.AnnounceNone; + void storeStaleRoutes(final Set gracefulTables) { + final CountDownLatch latch = new CountDownLatch(gracefulTables.size()); + + try (DOMDataReadOnlyTransaction tx = this.chain.getDomChain().newReadOnlyTransaction()) { + for (TablesKey tablesKey : gracefulTables) { + final TableContext ctx = this.tables.get(tablesKey); + if (ctx == null) { + LOG.warn("Missing table for address family {}", tablesKey); + latch.countDown(); + continue; + } + + Futures.addCallback(tx.read(LogicalDatastoreType.OPERATIONAL, ctx.routesPath()), + new FutureCallback>>() { + @Override + public void onSuccess(final Optional> routesOptional) { + try { + if (routesOptional.isPresent()) { + synchronized (AdjRibInWriter.this.staleRoutesRegistry) { + final MapNode routesNode = (MapNode) routesOptional.get(); + final List routes = routesNode.getValue().stream() + .map(MapEntryNode::getIdentifier) + .collect(Collectors.toList()); + if (!routes.isEmpty()) { + AdjRibInWriter.this.staleRoutesRegistry.put(tablesKey, routes); + } + } + } + } finally { + latch.countDown(); + } + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.warn("Failed to store stale routes for table {}", tablesKey, throwable); + latch.countDown(); + } + }, MoreExecutors.directExecutor()); + } + } + + try { + latch.await(); + } catch (InterruptedException e) { + LOG.warn("Interrupted while waiting to store stale routes with {} tasks of {} to finish", latch.getCount(), + gracefulTables, e); + } } - static boolean isLearnNone(final java.util.Optional peerStatus) { - return peerStatus.isPresent() && peerStatus.get() == SimpleRoutingPolicy.LearnNone; + void removeStaleRoutes(final TablesKey tableKey) { + final TableContext ctx = this.tables.get(tableKey); + if (ctx == null) { + LOG.debug("No table for {}, not removing any stale routes", tableKey); + return; + } + final Collection routeKeys = this.staleRoutesRegistry.get(tableKey); + if (routeKeys == null || routeKeys.isEmpty()) { + LOG.debug("No stale routes present in table {}", tableKey); + return; + } + LOG.trace("Removing routes {}", routeKeys); + final DOMDataWriteTransaction tx = this.chain.getDomChain().newWriteOnlyTransaction(); + routeKeys.forEach(routeKey -> { + tx.delete(LogicalDatastoreType.OPERATIONAL, ctx.routePath(routeKey)); + }); + final FluentFuture future = tx.commit(); + this.submitted = future; + future.addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.trace("Removing routes {}, succeed", routeKeys); + synchronized (AdjRibInWriter.this.staleRoutesRegistry) { + staleRoutesRegistry.remove(tableKey); + } + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.warn("Removing routes {}, failed", routeKeys, throwable); + } + }, MoreExecutors.directExecutor()); } - private static String simpleRoutingPolicyString(final SimpleRoutingPolicy simpleRoutingPolicy) { - switch (simpleRoutingPolicy) { - case AnnounceNone: - return "announce-none"; - case LearnNone: - return "learn-none"; - default: - throw new IllegalArgumentException("Unhandled Simple Routing Policy " + simpleRoutingPolicy); + FluentFuture clearTables(final Set tablesToClear) { + if (tablesToClear == null || tablesToClear.isEmpty()) { + return CommitInfo.emptyFluentFuture(); } + + final DOMDataWriteTransaction wtx = this.chain.getDomChain().newWriteOnlyTransaction(); + tablesToClear.forEach(tableKey -> { + final TableContext ctx = this.tables.get(tableKey); + wtx.delete(LogicalDatastoreType.OPERATIONAL, ctx.routesPath().getParent()); + }); + return wtx.commit(); } }