X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=bgp%2Frib-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fprotocol%2Fbgp%2Frib%2Fimpl%2FLocRibWriter.java;h=981307e1acce4119c4a698145d67d167696ea904;hb=8e1600ff2c9d6d1f1c007e7329c5af4bfd7077b8;hp=3b0ed261e57790e91a3ff66e994585c160011440;hpb=00d97b74f846b589c3740717049e6982e8577066;p=bgpcep.git diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java index 3b0ed261e5..981307e1ac 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java @@ -7,9 +7,10 @@ */ package org.opendaylight.protocol.bgp.rib.impl; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.primitives.UnsignedInteger; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -22,28 +23,33 @@ 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.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry; import org.opendaylight.protocol.bgp.rib.spi.RIBSupport; import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber; 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.bgp.rib.rib.LocRib; 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.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.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.rib.rev130925.rib.tables.Routes; -import org.opendaylight.yangtools.yang.binding.util.BindingReflections; +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; 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.YangInstanceIdentifier.PathArgument; 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.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.ImmutableNodes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,99 +58,214 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class); - private final Map routeEntries = new HashMap<>(); - private final YangInstanceIdentifier target; + private static final LeafNode ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.TRUE); + private static final NodeIdentifier ROUTES_IDENTIFIER = new NodeIdentifier(Routes.QNAME); + private static final NodeIdentifier EFFRIBIN_NID = new NodeIdentifier(EffectiveRibIn.QNAME); + private static final NodeIdentifier TABLES_NID = new NodeIdentifier(Tables.QNAME); + + private final Map routeEntries = new HashMap<>(); + private final YangInstanceIdentifier locRibTarget; private final DOMTransactionChain chain; private final ExportPolicyPeerTracker peerPolicyTracker; private final NodeIdentifier attributesIdentifier; private final Long ourAs; private final RIBSupport ribSupport; + private final NodeIdentifierWithPredicates tableKey; + private final TablesKey localTablesKey; + private final RIBSupportContextRegistry registry; + private final ListenerRegistration reg; - LocRibWriter(final RIBSupport ribSupport, final DOMTransactionChain chain, final YangInstanceIdentifier target, final Long ourAs, - final DOMDataTreeChangeService service, final PolicyDatabase pd) { + private LocRibWriter(final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier target, final Long ourAs, + final DOMDataTreeChangeService service, final PolicyDatabase pd, final TablesKey tablesKey) { this.chain = Preconditions.checkNotNull(chain); - this.target = Preconditions.checkNotNull(target); + this.tableKey = RibSupportUtils.toYangTablesKey(tablesKey); + this.localTablesKey = tablesKey; + this.locRibTarget = YangInstanceIdentifier.create(target.node(LocRib.QNAME).node(Tables.QNAME).node(this.tableKey).getPathArguments()); this.ourAs = Preconditions.checkNotNull(ourAs); - this.attributesIdentifier = ribSupport.routeAttributesIdentifier(); - this.peerPolicyTracker = new ExportPolicyPeerTracker(service, target, pd); - this.ribSupport = ribSupport; + this.registry = registry; + this.ribSupport = this.registry.getRIBSupportContext(tablesKey).getRibSupport(); + this.attributesIdentifier = this.ribSupport.routeAttributesIdentifier(); + this.peerPolicyTracker = new ExportPolicyPeerTracker(pd); + + final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); + tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Routes.QNAME), this.ribSupport.emptyRoutes()); + tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE); + tx.submit(); + + final YangInstanceIdentifier tableId = target.node(Peer.QNAME).node(Peer.QNAME); - service.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, target), this); + this.reg = service.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId), this); } - public static LocRibWriter create(@Nonnull final RIBSupport ribSupport, @Nonnull final TablesKey tablesKey, @Nonnull final DOMTransactionChain chain, @Nonnull final YangInstanceIdentifier target, + public static LocRibWriter create(@Nonnull final RIBSupportContextRegistry registry, @Nonnull final TablesKey tablesKey, @Nonnull final DOMTransactionChain chain, @Nonnull final YangInstanceIdentifier target, @Nonnull final AsNumber ourAs, @Nonnull final DOMDataTreeChangeService service, @Nonnull final PolicyDatabase pd) { + return new LocRibWriter(registry, chain, target, ourAs.getValue(), service, pd, tablesKey); + } - final YangInstanceIdentifier tableId = target.node(Peer.QNAME).node(Peer.QNAME).node(EffectiveRibIn.QNAME).node(Tables.QNAME).node(RibSupportUtils.toYangTablesKey(tablesKey)); - - final QName list = BindingReflections.findQName(ribSupport.routesListClass()); - final YangInstanceIdentifier routeId = tableId.node(Routes.QNAME).node(BindingReflections.findQName(ribSupport.routesContainerClass())).node(list); + @Override + public void close() { + this.reg.close(); + // FIXME: wipe the local rib + // FIXME: wait for the chain to close? unfortunately RIBImpl is the listener, so that may require some work + this.chain.close(); + } - return new LocRibWriter(ribSupport, chain, routeId, ourAs.getValue(), service, pd); + @Nonnull + private AbstractRouteEntry createEntry(final PathArgument routeId) { + final AbstractRouteEntry ret = this.ribSupport.isComplexRoute() ? new ComplexRouteEntry() : new SimpleRouteEntry(); + this.routeEntries.put(routeId, ret); + LOG.trace("Created new entry for {}", routeId); + return ret; } @Override - public void close() { - this.peerPolicyTracker.close(); + public void onDataTreeChanged(final Collection changes) { + LOG.trace("Received data change {} to LocRib {}", changes, this); + + final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); + try { + /* + * We use two-stage processing here in hopes that we avoid duplicate + * calculations when multiple peers have changed a particular entry. + */ + final Map toUpdate = update(tx, changes); + + // Now walk all updated entries + walkThrough(tx, toUpdate); + } catch (final Exception e) { + LOG.error("Failed to completely propagate updates {}, state is undefined", changes, e); + } finally { + tx.submit(); + } } - private static void printChildren(final DataTreeCandidateNode root) { - LOG.debug("Candidate node {} type {} identifier {}", root, root.getModificationType(), root.getIdentifier()); - if (ModificationType.WRITE.equals(root.getModificationType())) { - LOG.debug("Data after {}", root.getDataAfter()); + private Map update(final DOMDataWriteTransaction tx, + final Collection changes) { + final Map ret = new HashMap<>(); + + for (final DataTreeCandidate tc : changes) { + final YangInstanceIdentifier rootPath = tc.getRootPath(); + final DataTreeCandidateNode rootNode = tc.getRootNode(); + //Perform first PeerRoleChange, in case where peer is not deleted, since a new + //peer added needs to be add to peerPolicyTracker before process tables + if (!rootNode.getModificationType().equals(ModificationType.DELETE)) { + filterOutPeerRole(rootNode, rootPath); + } + filterOutChangesToSupportedTables(rootNode, rootPath, tx); + filterOutAnyChangeOutsideEffRibsIn(rootNode, rootPath, ret, tx); + //If Peer is removed we need to filterOutPeerRole after all routes are removed, then peer can + // be removed from peerPolicyTracker + if (rootNode.getModificationType().equals(ModificationType.DELETE)) { + filterOutPeerRole(rootNode, rootPath); + } } - if (root.getChildNodes().isEmpty()) { + + return ret; + } + + private void filterOutAnyChangeOutsideEffRibsIn(final DataTreeCandidateNode rootNode, final YangInstanceIdentifier rootPath, + final Map ret, final DOMDataWriteTransaction tx) { + final DataTreeCandidateNode ribIn = rootNode.getModifiedChild(EFFRIBIN_NID); + if (ribIn == null) { + LOG.debug("Skipping change {}", rootNode.getIdentifier()); return; } - for (final DataTreeCandidateNode child : root.getChildNodes()) { - printChildren(child); + final DataTreeCandidateNode table = ribIn.getModifiedChild(TABLES_NID).getModifiedChild(this.tableKey); + if (table == null) { + LOG.debug("Skipping change {}", rootNode.getIdentifier()); + return; } + final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath); + final PeerId peerId = IdentifierUtils.peerId(peerKey); + updateNodes(table, peerId, tx, ret); } - @Override - public void onDataTreeChanged(final Collection changes) { - LOG.trace("Received data change to LocRib {}", Arrays.toString(changes.toArray())); - /* - * We use two-stage processing here in hopes that we avoid duplicate - * calculations when multiple peers have changed a particular entry. - */ - final Map toUpdate = new HashMap<>(); - for (final DataTreeCandidate tc : changes) { - printChildren(tc.getRootNode()); + private void filterOutChangesToSupportedTables(final DataTreeCandidateNode rootNode, final YangInstanceIdentifier rootPath, final DOMDataWriteTransaction tx) { + final DataTreeCandidateNode tablesChange = rootNode.getModifiedChild(AbstractPeerRoleTracker.PEER_TABLES); - final YangInstanceIdentifier path = tc.getRootPath(); - final PathArgument routeId = path.getLastPathArgument(); - final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(path); - final PeerId peerId = IdentifierUtils.peerId(peerKey); - final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId); + if (tablesChange != null) { + final PeerId peerIdOfNewPeer = IdentifierUtils.peerId((NodeIdentifierWithPredicates) IdentifierUtils.peerPath(rootPath).getLastPathArgument()); + final PeerRole newPeerRole = this.peerPolicyTracker.getRole(IdentifierUtils.peerPath(rootPath)); + final PeerExportGroup peerGroup = this.peerPolicyTracker.getPeerGroup(newPeerRole); - RouteEntry entry = this.routeEntries.get(routeId); - if (tc.getRootNode().getDataAfter().isPresent()) { - if (entry == null) { - entry = new RouteEntry(); - this.routeEntries.put(routeId, entry); - LOG.trace("Created new entry for {}", routeId); + for (final DataTreeCandidateNode node : tablesChange.getChildNodes()) { + final boolean supportedTableAdded = this.peerPolicyTracker.onTablesChanged(peerIdOfNewPeer, node); + if (supportedTableAdded) { + for (Map.Entry entry : this.routeEntries.entrySet()) { + if(isTableSupported(peerIdOfNewPeer)) { + final AbstractRouteEntry routeEntry = entry.getValue(); + final ContainerNode attributes = routeEntry.attributes(); + final PathArgument routeId = entry.getKey(); + final YangInstanceIdentifier routeTarget = getRouteTarget(rootPath, routeId); + final NormalizedNode value = routeEntry.createValue(routeId); + final PeerId routePeerId = RouterIds.createPeerId(routeEntry.getBestRouterId()); + final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(routePeerId, attributes); + + if (effectiveAttributes != null && value != null) { + LOG.debug("Write route {} to peer AdjRibsOut {}", value, peerIdOfNewPeer); + tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value); + tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(this.attributesIdentifier), effectiveAttributes); + } + } + } + } + } + } + } + + private YangInstanceIdentifier getRouteTarget(final YangInstanceIdentifier rootPath, final PathArgument routeId) { + return this.ribSupport.routePath(rootPath.node(AdjRibOut.QNAME).node(Tables.QNAME).node(this.tableKey).node(ROUTES_IDENTIFIER), routeId); + } + + private void filterOutPeerRole(final DataTreeCandidateNode rootNode, final YangInstanceIdentifier rootPath) { + final DataTreeCandidateNode roleChange = rootNode.getModifiedChild(AbstractPeerRoleTracker.PEER_ROLE_NID); + if (roleChange != null) { + this.peerPolicyTracker.onDataTreeChanged(roleChange, IdentifierUtils.peerPath(rootPath)); + } + } + + private void updateNodes(final DataTreeCandidateNode table, final PeerId peerId, final DOMDataWriteTransaction tx, final Map routes) { + for (final DataTreeCandidateNode child : table.getChildNodes()) { + LOG.debug("Modification type {}", child.getModificationType()); + if ((Attributes.QNAME).equals(child.getIdentifier().getNodeType())) { + if (child.getDataAfter().isPresent()) { + // putting uptodate attribute in + LOG.trace("Uptodate found for {}", child.getDataAfter()); + tx.put(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(child.getIdentifier()), child.getDataAfter().get()); } + continue; + } + updateRoutesEntries(child, peerId, routes); + } + } + + private void updateRoutesEntries(final DataTreeCandidateNode child, final PeerId peerId, final Map routes) { + final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId); + for (final DataTreeCandidateNode route : this.ribSupport.changedRoutes(child)) { + final PathArgument routeId = route.getIdentifier(); + AbstractRouteEntry entry = this.routeEntries.get(routeId); - final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(tc.getRootNode().getDataAfter(), this.ribSupport.routeAttributesIdentifier()).orNull(); - entry.addRoute(routerId, advertisedAttrs); - LOG.trace("Added route from {} attributes{}", routerId, advertisedAttrs); + final Optional> maybeData = route.getDataAfter(); + if (maybeData.isPresent()) { + if (entry == null) { + entry = createEntry(routeId); + } + entry.addRoute(routerId, this.attributesIdentifier, maybeData.get()); } else if (entry != null && entry.removeRoute(routerId)) { this.routeEntries.remove(routeId); entry = null; LOG.trace("Removed route from {}", routerId); } - LOG.debug("Updated route {} entry {}", routeId, entry); - toUpdate.put(new RouteUpdateKey(peerId, routeId), entry); + routes.put(new RouteUpdateKey(peerId, routeId), entry); } + } - final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); - - // Now walk all updated entries - for (final Entry e : toUpdate.entrySet()) { + private void walkThrough(final DOMDataWriteTransaction tx, final Map toUpdate) { + for (final Entry e : toUpdate.entrySet()) { LOG.trace("Walking through {}", e); - final RouteEntry entry = e.getValue(); + final AbstractRouteEntry entry = e.getValue(); + final RouteUpdateKey key = e.getKey(); final NormalizedNode value; if (entry != null) { @@ -153,52 +274,64 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { LOG.trace("Continuing"); continue; } - value = entry.bestValue(e.getKey().getRouteId()); + value = entry.createValue(key.getRouteId()); LOG.trace("Selected best value {}", value); } else { value = null; } + final YangInstanceIdentifier writePath = this.ribSupport.routePath(this.locRibTarget.node(ROUTES_IDENTIFIER), key.getRouteId()); if (value != null) { LOG.debug("Write route to LocRib {}", value); - tx.put(LogicalDatastoreType.OPERATIONAL, this.target.node(e.getKey().getRouteId()), value); + tx.put(LogicalDatastoreType.OPERATIONAL, writePath, value); } else { LOG.debug("Delete route from LocRib {}", entry); - tx.delete(LogicalDatastoreType.OPERATIONAL, this.target.node(e.getKey().getRouteId())); + tx.delete(LogicalDatastoreType.OPERATIONAL, writePath); } + fillAdjRibsOut(tx, entry, value, key); + } + } - /* - * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to - * expose from which client a particular route was learned from in the local RIB, and have - * the listener perform filtering. - * - * We walk the policy set in order to minimize the amount of work we do for multiple peers: - * if we have two eBGP peers, for example, there is no reason why we should perform the translation - * multiple times. - */ - for (final PeerRole role : PeerRole.values()) { - final PeerExportGroup peerGroup = this.peerPolicyTracker.getPeerGroup(role); - if (peerGroup != null) { - final ContainerNode attributes = null; - final PeerId peerId = e.getKey().getPeerId(); - final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(peerId, attributes); - + @VisibleForTesting + private void fillAdjRibsOut(final DOMDataWriteTransaction tx, final AbstractRouteEntry entry, final NormalizedNode value, final RouteUpdateKey key) { + /* + * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to + * expose from which client a particular route was learned from in the local RIB, and have + * the listener perform filtering. + * + * We walk the policy set in order to minimize the amount of work we do for multiple peers: + * if we have two eBGP peers, for example, there is no reason why we should perform the translation + * multiple times. + */ + for (final PeerRole role : PeerRole.values()) { + final PeerExportGroup peerGroup = this.peerPolicyTracker.getPeerGroup(role); + if (peerGroup != null) { + final PeerId peerId = key.getPeerId(); + final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(peerId, entry.attributes()); + if (effectiveAttributes != null) { for (final Entry pid : peerGroup.getPeers()) { - // This points to adj-rib-out for a particular peer/table combination - final YangInstanceIdentifier routeTarget = pid.getValue().node(e.getKey().getRouteId()); - - if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) { - LOG.debug("Write route to AdjRibsOut {}", value); - tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value); - tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(this.attributesIdentifier), effectiveAttributes); - } else { - tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget); + if (!peerId.equals(pid.getKey()) && isTableSupported(pid.getKey())) { + final YangInstanceIdentifier routeTarget = getRouteTarget(pid.getValue(), key.getRouteId()); + if (value != null) { + LOG.debug("Write route {} to peers AdjRibsOut {}", value, pid.getKey()); + tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value); + tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(this.attributesIdentifier), effectiveAttributes); + } else { + LOG.trace("Removing {} from transaction for peer {}", routeTarget, pid.getKey()); + tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget); + } } } } } } + } - tx.submit(); + private boolean isTableSupported(final PeerId key) { + if (!this.peerPolicyTracker.isTableSupported(key, this.localTablesKey)) { + LOG.trace("Route rejected, peer {} does not support this table type {}", key, this.localTablesKey); + return false; + } + return true; } }