From ac244dd5835c74be9a0f1f3855f4bdbfbf7e93e7 Mon Sep 17 00:00:00 2001 From: Dana Kutenicsova Date: Wed, 8 Jul 2015 09:19:34 +0200 Subject: [PATCH] BUG-3840 : introduced Supported tables When advertizing to other peers, we need to keep track of afi/safi that a client supports. Not having them causes exceptions in the logs, as the particular AdjRibsOut was not created. Change-Id: I3bea92666c200c8dceb83e3c4f144b9afe1f0305 Signed-off-by: Dana Kutenicsova --- bgp/rib-api/src/main/yang/bgp-rib.yang | 5 +- .../bgp/rib/impl/AbstractPeerRoleTracker.java | 2 + .../protocol/bgp/rib/impl/AdjRibInWriter.java | 35 +++- .../bgp/rib/impl/ExportPolicyPeerTracker.java | 23 +++ .../protocol/bgp/rib/impl/LocRibWriter.java | 22 ++- .../bgp/rib/impl/AdjRibsInWriterTest.java | 87 ++++++++++ .../bgp/rib/impl/LocRibWriterTest.java | 160 ++++++++++++++++++ .../protocol/bgp/rib/spi/RibSupportUtils.java | 25 ++- .../bgp/rib/spi/RibSupportUtilsTest.java | 25 ++- 9 files changed, 366 insertions(+), 18 deletions(-) create mode 100644 bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibsInWriterTest.java create mode 100644 bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriterTest.java diff --git a/bgp/rib-api/src/main/yang/bgp-rib.yang b/bgp/rib-api/src/main/yang/bgp-rib.yang index c7b03abe75..f7479f9e81 100644 --- a/bgp/rib-api/src/main/yang/bgp-rib.yang +++ b/bgp/rib-api/src/main/yang/bgp-rib.yang @@ -103,7 +103,10 @@ module bgp-rib { type peer-role; mandatory true; } - + list supported-tables { + key "afi safi"; + uses bgp-mp:bgp-table-type; + } container adj-rib-in { description "Routes as we have received them from the peer."; uses rib; diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java index 810e00c7e3..880333f622 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java @@ -12,6 +12,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; 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.Peer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.SupportedTables; import org.opendaylight.yangtools.yang.binding.BindingMapping; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -47,6 +48,7 @@ abstract class AbstractPeerRoleTracker { } static final NodeIdentifier PEER_ROLE_NID = new NodeIdentifier(QName.cachedReference(QName.create(Peer.QNAME, "peer-role"))); + static final NodeIdentifier PEER_TABLES = new NodeIdentifier(SupportedTables.QNAME); protected AbstractPeerRoleTracker() { } 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 bf1bca6cbe..2f5169861b 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,6 +7,7 @@ */ package org.opendaylight.protocol.bgp.rib.impl; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; @@ -31,6 +32,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib. 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; @@ -44,7 +46,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; 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; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,16 +59,18 @@ import org.slf4j.LoggerFactory; @NotThreadSafe final class AdjRibInWriter { private static final Logger LOG = LoggerFactory.getLogger(AdjRibInWriter.class); - - private static final LeafNode ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE); + @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); - private static final QName PEER_ID_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-id")); + @VisibleForTesting + static final QName PEER_ID_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-id")); private static final QName PEER_ROLE_QNAME = QName.cachedReference(QName.create(Peer.QNAME, "peer-role")); private static final NodeIdentifier ADJRIBIN = new NodeIdentifier(AdjRibIn.QNAME); private static final NodeIdentifier ADJRIBOUT = new NodeIdentifier(AdjRibOut.QNAME); private static final NodeIdentifier EFFRIBIN = new NodeIdentifier(EffectiveRibIn.QNAME); private static final NodeIdentifier PEER_ID = new NodeIdentifier(PEER_ID_QNAME); private static final NodeIdentifier PEER_ROLE = new NodeIdentifier(PEER_ROLE_QNAME); + private static final NodeIdentifier PEER_TABLES = new NodeIdentifier(SupportedTables.QNAME); private static final NodeIdentifier TABLES = new NodeIdentifier(Tables.QNAME); // FIXME: is there a utility method to construct this? @@ -128,7 +134,7 @@ final class AdjRibInWriter { final YangInstanceIdentifier newPeerPath; if (!newPeerId.equals(this.peerId)) { - newPeerPath = this.createEmptyPeerStructure(newPeerId, isAppPeer, tx); + newPeerPath = createEmptyPeerStructure(newPeerId, isAppPeer, tx); } else { newPeerPath = this.peerPath; @@ -154,9 +160,14 @@ final class AdjRibInWriter { } // install tables for adj-ribs-out (we do not need TableContext for that if (!isAppPeer) { + final NodeIdentifierWithPredicates supTablesKey = RibSupportUtils.toYangKey(SupportedTables.QNAME, k); + final DataContainerNodeAttrBuilder tt = Builders.mapEntryBuilder().withNodeIdentifier(supTablesKey); + for (final Entry e : supTablesKey.getKeyValues().entrySet()) { + tt.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue())); + } + tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath.node(PEER_TABLES).node(supTablesKey), tt.build()); rs.clearTable(tx, newPeerPath.node(EMPTY_ADJRIBOUT.getIdentifier()).node(TABLES).node(key)); } - // We will use table keys very often, make sure they are optimized final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(newPeerPath.node(EMPTY_ADJRIBIN.getIdentifier()).node(TABLES)); idb.nodeWithKey(key.getNodeType(), key.getKeyValues()); @@ -184,18 +195,24 @@ final class AdjRibInWriter { 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(), isAppPeer)); + LOG.debug("New peer {} structure installed.", newPeerPath); + return newPeerPath; + } + + @VisibleForTesting + MapEntryNode peerSkeleton(final NodeIdentifierWithPredicates peerKey, final String peerId, final boolean isAppPeer) { final DataContainerNodeBuilder pb = Builders.mapEntryBuilder(); pb.withNodeIdentifier(peerKey); - pb.withChild(ImmutableNodes.leafNode(PEER_ID, newPeerId.getValue())); + pb.withChild(ImmutableNodes.leafNode(PEER_ID, peerId)); pb.withChild(ImmutableNodes.leafNode(PEER_ROLE, this.role)); + pb.withChild(ImmutableMapNodeBuilder.create().withNodeIdentifier(PEER_TABLES).build()); pb.withChild(EMPTY_ADJRIBIN); pb.withChild(EMPTY_EFFRIBIN); if (!isAppPeer) { pb.withChild(EMPTY_ADJRIBOUT); } - tx.put(LogicalDatastoreType.OPERATIONAL, newPeerPath, pb.build()); - LOG.debug("New peer {} structure installed.", newPeerPath); - return newPeerPath; + return pb.build(); } /** diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java index b9d329fa95..15aa8a77dc 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java @@ -11,6 +11,7 @@ import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; +import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; @@ -23,8 +24,10 @@ import java.util.Map; import java.util.Map.Entry; 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.rib.TablesKey; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +45,7 @@ final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker { }; private final Map peerRoles = new HashMap<>(); + private final HashMultimap peerTables = HashMultimap.create(); private volatile Map groups = Collections.emptyMap(); private final PolicyDatabase policyDatabase; @@ -98,4 +102,23 @@ final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker { PeerExportGroup getPeerGroup(final PeerRole role) { return this.groups.get(Preconditions.checkNotNull(role)); } + + void onTablesChanged(final DataTreeCandidateNode change, final YangInstanceIdentifier peerPath) { + final PeerId peerId = IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerPath.getLastPathArgument()); + for (final DataTreeCandidateNode node : change.getChildNodes()) { + if (node.getDataAfter().isPresent()) { + final NodeIdentifierWithPredicates value = (NodeIdentifierWithPredicates) node.getDataAfter().get().getIdentifier(); + final boolean added = this.peerTables.put(peerId, value); + if (added) { + LOG.debug("Supported table {} added to peer {}", value, peerId); + } + } else { + LOG.debug("Removed tables {} from peer {}", this.peerTables.removeAll(peerId), peerId); + } + } + } + + boolean isTableSupported(final PeerId peerId, final TablesKey tablesKey) { + return this.peerTables.get(peerId).contains(tablesKey); + } } \ No newline at end of file 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 5b827b4cb4..be209349e7 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,6 +7,7 @@ */ 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; @@ -69,6 +70,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { private final Long ourAs; private final RIBSupport ribSupport; private final NodeIdentifierWithPredicates tableKey; + private final TablesKey localTablesKey; private final RIBSupportContextRegistry registry; private final ListenerRegistration reg; @@ -76,6 +78,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { final DOMDataTreeChangeService service, final PolicyDatabase pd, final TablesKey tablesKey) { this.chain = Preconditions.checkNotNull(chain); 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.registry = registry; @@ -106,9 +109,9 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { this.chain.close(); } - @Nonnull private AbstractRouteEntry createEntry(final PathArgument routeId) { + @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; @@ -116,7 +119,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { @Override public void onDataTreeChanged(final Collection changes) { - LOG.trace("Received data change to LocRib {}", changes); + LOG.trace("Received data change {} to LocRib {}", changes, this); final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction(); try { @@ -142,6 +145,12 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { for (final DataTreeCandidate tc : changes) { final YangInstanceIdentifier rootPath = tc.getRootPath(); final DataTreeCandidateNode rootNode = tc.getRootNode(); + // filter out changes to supported tables + final DataTreeCandidateNode tablesChange = rootNode.getModifiedChild(AbstractPeerRoleTracker.PEER_TABLES); + if (tablesChange != null) { + this.peerPolicyTracker.onTablesChanged(tablesChange, IdentifierUtils.peerPath(rootPath)); + } + // filter out peer role final DataTreeCandidateNode roleChange = rootNode.getModifiedChild(AbstractPeerRoleTracker.PEER_ROLE_NID); if (roleChange != null) { this.peerPolicyTracker.onDataTreeChanged(roleChange, IdentifierUtils.peerPath(rootPath)); @@ -233,7 +242,8 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { } } - private void fillAdjRibsOut(final DOMDataWriteTransaction tx, final AbstractRouteEntry entry, final NormalizedNode value, final RouteUpdateKey key) { + @VisibleForTesting + 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 @@ -248,6 +258,10 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { if (peerGroup != null) { final ContainerNode attributes = entry == null ? null : entry.attributes(); final PeerId peerId = key.getPeerId(); + if (!this.peerPolicyTracker.isTableSupported(peerId, this.localTablesKey)) { + LOG.trace("Route rejected, peer {} does not support this table type {}", peerId, this.tableKey); + continue; + } final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(peerId, attributes); for (final Entry pid : peerGroup.getPeers()) { final YangInstanceIdentifier routeTarget = this.ribSupport.routePath(pid.getValue().node(AdjRibOut.QNAME).node(Tables.QNAME).node(this.tableKey).node(ROUTES_IDENTIFIER), key.getRouteId()); diff --git a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibsInWriterTest.java b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibsInWriterTest.java new file mode 100644 index 0000000000..c095d35b86 --- /dev/null +++ b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibsInWriterTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.bgp.rib.impl; + +import static org.junit.Assert.assertNotNull; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +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.RIBSupportContext; +import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry; +import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils; +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.Rib; +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.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.types.rev130919.Ipv4AddressFamily; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class AdjRibsInWriterTest { + + @Mock + private DOMTransactionChain chain; + + @Mock + private DOMDataWriteTransaction tx; + + @Mock + private RIBSupportContextRegistry registry; + + @Mock + private RIBSupportContext context; + + private AdjRibInWriter writer; + + private static final TablesKey k4 = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class); + private final Set tableTypes = Sets.newHashSet(k4); + private final String peerIp = "12.34.56.78"; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + this.writer = AdjRibInWriter.create(YangInstanceIdentifier.of(Rib.QNAME), PeerRole.Ebgp, this.chain); + assertNotNull(this.writer); + Mockito.doReturn("MockedTrans").when(this.tx).toString(); + Mockito.doReturn(this.tx).when(this.chain).newWriteOnlyTransaction(); + Mockito.doReturn(Mockito.mock(CheckedFuture.class)).when(this.tx).submit(); + } + + @Test + public void testTransform() { + final YangInstanceIdentifier peerPath = YangInstanceIdentifier.builder().node(Rib.QNAME).node(Peer.QNAME).nodeWithKey(Peer.QNAME, AdjRibInWriter.PEER_ID_QNAME, this.peerIp).build(); + Mockito.doNothing().when(this.tx).put(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.any(YangInstanceIdentifier.class), Mockito.any(NormalizedNode.class)); + Mockito.doNothing().when(this.tx).merge(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.any(YangInstanceIdentifier.class), Mockito.any(NormalizedNode.class)); + Mockito.doReturn(this.context).when(this.registry).getRIBSupportContext(Mockito.any(TablesKey.class)); + Mockito.doNothing().when(this.context).clearTable(Mockito.eq(this.tx), Mockito.any(YangInstanceIdentifier.class)); + + this.writer.transform(new PeerId(this.peerIp), this.registry, this.tableTypes, false); + + // verify peer skeleton was inserted correctly + Mockito.verify(this.tx).put(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.eq(peerPath), Mockito.eq(this.writer.peerSkeleton(IdentifierUtils.peerKey(peerPath), this.peerIp, false))); + // verify supported tables were inserted for ipv4 + Mockito.verify(this.tx).put(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.eq(peerPath.node(SupportedTables.QNAME).node(RibSupportUtils.toYangKey(SupportedTables.QNAME, k4))), Mockito.any(NormalizedNode.class)); + // verify uptodate set to false + final YangInstanceIdentifier path = peerPath.node(AdjRibIn.QNAME).node(Tables.QNAME).node(RibSupportUtils.toYangTablesKey(k4)).node(Attributes.QNAME).node(AdjRibInWriter.ATTRIBUTES_UPTODATE_FALSE.getNodeType()); + Mockito.verify(this.tx).merge(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.eq(path), Mockito.eq(AdjRibInWriter.ATTRIBUTES_UPTODATE_FALSE)); + } +} \ No newline at end of file diff --git a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriterTest.java b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriterTest.java new file mode 100644 index 0000000000..5dc113b0ae --- /dev/null +++ b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriterTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.bgp.rib.impl; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener; +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.RIBSupportContext; +import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry; +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.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.routes.Ipv4Routes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.routes.ipv4.routes.Ipv4Route; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib; +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.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.TablesKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +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.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; + +public class LocRibWriterTest { + + @Mock + DOMDataWriteTransaction domTransWrite; + + private final PolicyDatabase pd = new PolicyDatabase((long) 35, new Ipv4Address("10.25.2.9"), new ClusterIdentifier(new Ipv4Address("10.25.2.9"))); + + @Mock + private RIBSupportContextRegistry registry; + + @Mock + private DOMTransactionChain chain; + + @Mock + private DOMDataTreeChangeService service; + + @Mock + private RIBSupportContext context; + + @Mock + private AbstractIPRIBSupport ribSupport; + + @Mock + CheckedFuture future; + + private LocRibWriter locRibWriter; + + private final List routes = new ArrayList<>(); + private final TablesKey tablesKey = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + Mockito.doAnswer(new Answer() { + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + final Object[] args = invocation.getArguments(); + final NormalizedNode node = (NormalizedNode) args[2]; + if (node.getNodeType().equals(Ipv4Route.QNAME) || node.getNodeType().equals(IPv4RIBSupport.PREFIX_QNAME)) { + LocRibWriterTest.this.routes.add((YangInstanceIdentifier) args[1]); + } + return args[1]; + } + }).when(this.domTransWrite).put(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.any(YangInstanceIdentifier.class), Mockito.any(NormalizedNode.class)); + Mockito.doAnswer(new Answer() { + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + final Object[] args = invocation.getArguments(); + final NormalizedNode node = (NormalizedNode) args[2]; + if (node.getNodeType().equals(Ipv4Route.QNAME) || node.getNodeType().equals(IPv4RIBSupport.PREFIX_QNAME)) { + LocRibWriterTest.this.routes.add((YangInstanceIdentifier) args[1]); + } + return args[1]; + } + }).when(this.domTransWrite).merge(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.any(YangInstanceIdentifier.class), Mockito.any(NormalizedNode.class)); + Mockito.doAnswer(new Answer() { + + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + final Object[] args = invocation.getArguments(); + LocRibWriterTest.this.routes.remove(args[1]); + return args[1]; + } + }).when(this.domTransWrite).delete(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.any(YangInstanceIdentifier.class)); + Mockito.doReturn(this.context).when(this.registry).getRIBSupportContext(this.tablesKey); + Mockito.doReturn(this.ribSupport).when(this.context).getRibSupport(); + Mockito.doReturn(this.domTransWrite).when(this.chain).newWriteOnlyTransaction(); + Mockito.doReturn(this.future).when(this.domTransWrite).submit(); + Mockito.doReturn(null).when(this.service).registerDataTreeChangeListener(Mockito.any(DOMDataTreeIdentifier.class), Mockito.any(DOMDataTreeChangeListener.class)); + Mockito.doReturn(Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(Routes.QNAME)).addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(Ipv4Routes.QNAME)).withChild(ImmutableNodes.mapNodeBuilder(Ipv4Route.QNAME).build()).build()).build()).when(this.ribSupport).emptyRoutes(); + this.locRibWriter = LocRibWriter.create(this.registry, this.tablesKey, this.chain, YangInstanceIdentifier.of(BgpRib.QNAME), new AsNumber((long) 35), this.service, this.pd); + } + + private DataTreeCandidate prepareUpdate() { + final DataTreeCandidate candidate = Mockito.mock(DataTreeCandidate.class); + Mockito.doReturn("candidate").when(candidate).toString(); + final YangInstanceIdentifier rootPath = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Peer.QNAME).nodeWithKey(Peer.QNAME, AdjRibInWriter.PEER_ID_QNAME, "12.12.12.12").build(); + Mockito.doReturn(rootPath).when(candidate).getRootPath(); + return candidate; + } + + @Test + public void testUpdateSupportedTables() { + final DataTreeCandidate candidate = prepareUpdate(); + final DataTreeCandidateNode node = Mockito.mock(DataTreeCandidateNode.class); + Mockito.doReturn("node").when(node).toString(); + final DataTreeCandidateNode tableChange = Mockito.mock(DataTreeCandidateNode.class); + // add + final DataTreeCandidateNode table = Mockito.mock(DataTreeCandidateNode.class); + final NormalizedNode dataAfter = Mockito.mock(NormalizedNode.class); + Mockito.doReturn(RibSupportUtils.toYangTablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class)).when(dataAfter).getIdentifier(); + Mockito.doReturn(Optional.of(dataAfter)).when(table).getDataAfter(); + Mockito.doReturn(Lists.newArrayList(table)).when(tableChange).getChildNodes(); + Mockito.doReturn("table change").when(tableChange).toString(); + Mockito.doReturn(tableChange).when(node).getModifiedChild(YangInstanceIdentifier.of(SupportedTables.QNAME).getLastPathArgument()); + Mockito.doReturn(null).when(node).getModifiedChild(AbstractPeerRoleTracker.PEER_ROLE_NID); + Mockito.doReturn(null).when(node).getModifiedChild(YangInstanceIdentifier.of(EffectiveRibIn.QNAME).getLastPathArgument()); + Mockito.doReturn(node).when(candidate).getRootNode(); + this.locRibWriter.onDataTreeChanged(Lists.newArrayList(candidate)); + // delete + final DataTreeCandidateNode tableDelete = Mockito.mock(DataTreeCandidateNode.class); + Mockito.doReturn(Optional.absent()).when(tableDelete).getDataAfter(); + Mockito.doReturn(Lists.newArrayList(tableDelete)).when(tableChange).getChildNodes(); + Mockito.doReturn("table change").when(tableChange).toString(); + Mockito.doReturn(node).when(candidate).getRootNode(); + this.locRibWriter.onDataTreeChanged(Lists.newArrayList(candidate)); + Mockito.verify(node, Mockito.times(2)).getModifiedChild(YangInstanceIdentifier.of(SupportedTables.QNAME).getLastPathArgument()); + } +} diff --git a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtils.java b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtils.java index 269e16ada5..61f0e70ad4 100644 --- a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtils.java +++ b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtils.java @@ -34,10 +34,33 @@ public final class RibSupportUtils { */ public static NodeIdentifierWithPredicates toYangTablesKey(final Class afi, final Class safi) { + return toYangKey(Tables.QNAME, afi, safi); + } + + /** + * Creates Yang Instance Identifier path argument from supplied AFI and SAFI + * + * @param id QNAME representing node + * @param afi Class representing AFI + * @param safi Class representing SAFI + * @return NodeIdentifierWithPredicates of 'id' for specified AFI, SAFI combination. + */ + public static NodeIdentifierWithPredicates toYangKey(final QName id, final Class afi, final Class safi) { final ImmutableMap keyValues = ImmutableMap.of( AFI_QNAME, BindingReflections.findQName(afi), SAFI_QNAME, BindingReflections.findQName(safi)); - return new NodeIdentifierWithPredicates(Tables.QNAME, keyValues); + return new NodeIdentifierWithPredicates(id, keyValues); + } + + /** + * Creates Yang Instance Identifier path argument from supplied {@link TablesKey} + * + * @param id QNAME representing node + * @param k Tables key representing table. + * @return NodeIdentifierWithPredicates of 'id' for specified AFI, SAFI combination. + */ + public static NodeIdentifierWithPredicates toYangKey(final QName id, final TablesKey k) { + return toYangKey(id, k.getAfi(), k.getSafi()); } /** diff --git a/bgp/rib-spi/src/test/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtilsTest.java b/bgp/rib-spi/src/test/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtilsTest.java index ae7c4e78fd..5e72780453 100644 --- a/bgp/rib-spi/src/test/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtilsTest.java +++ b/bgp/rib-spi/src/test/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtilsTest.java @@ -1,13 +1,18 @@ package org.opendaylight.protocol.bgp.rib.spi; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.junit.Test; +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.types.rev130919.AddressFamily; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.common.QName; @@ -27,13 +32,27 @@ public class RibSupportUtilsTest { } @Test - public void test() { - final Class afi = Ipv4AddressFamily.class; - final Class safi = UnicastSubsequentAddressFamily.class; + public void testYangTablesKey() { + final Class afi = Ipv4AddressFamily.class; + final Class safi = UnicastSubsequentAddressFamily.class; final TablesKey k = new TablesKey(afi, safi); final NodeIdentifierWithPredicates p = RibSupportUtils.toYangTablesKey(k); final Map m = p.getKeyValues(); assertFalse(m.isEmpty()); + assertEquals(Tables.QNAME, p.getNodeType()); + assertTrue(m.containsValue(BindingReflections.findQName(afi))); + assertTrue(m.containsValue(BindingReflections.findQName(safi))); + } + + @Test + public void testYangKey() { + final Class afi = Ipv4AddressFamily.class; + final Class safi = UnicastSubsequentAddressFamily.class; + final TablesKey k = new TablesKey(afi, safi); + final NodeIdentifierWithPredicates p = RibSupportUtils.toYangKey(SupportedTables.QNAME, k); + final Map m = p.getKeyValues(); + assertFalse(m.isEmpty()); + assertEquals(SupportedTables.QNAME, p.getNodeType()); assertTrue(m.containsValue(BindingReflections.findQName(afi))); assertTrue(m.containsValue(BindingReflections.findQName(safi))); } -- 2.36.6