BUG-3840 : introduced Supported tables 91/24091/6
authorDana Kutenicsova <dkutenic@cisco.com>
Wed, 8 Jul 2015 07:19:34 +0000 (09:19 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 17 Jul 2015 08:23:58 +0000 (08:23 +0000)
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 <dkutenic@cisco.com>
bgp/rib-api/src/main/yang/bgp-rib.yang
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibInWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibsInWriterTest.java [new file with mode: 0644]
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriterTest.java [new file with mode: 0644]
bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtils.java
bgp/rib-spi/src/test/java/org/opendaylight/protocol/bgp/rib/spi/RibSupportUtilsTest.java

index c7b03abe751aa405d74381f63d24978958c8a923..f7479f9e818045b31f2e0830087535cd1928b6c5 100644 (file)
@@ -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;
index 810e00c7e3a55e409b82aaf70316b97a180492eb..880333f622701c058458cd540f99d3ff204e7088 100644 (file)
@@ -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() {
     }
index bf1bca6cbebb17daced7e77ed40021144026bf02..2f5169861b297fce42780954a308e9212d45cae3 100644 (file)
@@ -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<Boolean> ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE);
+    @VisibleForTesting
+    static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_FALSE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.FALSE);
     private static final LeafNode<Boolean> 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<NodeIdentifierWithPredicates, MapEntryNode> tt = Builders.mapEntryBuilder().withNodeIdentifier(supTablesKey);
+                    for (final Entry<QName, Object> 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<NodeIdentifierWithPredicates, MapEntryNode> 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();
     }
 
     /**
index b9d329fa95ea750c18b6692f604943cf9d7bde74..15aa8a77dce804b6b4410e0b763a5455958b15f0 100644 (file)
@@ -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<YangInstanceIdentifier, PeerRole> peerRoles = new HashMap<>();
+    private final HashMultimap<PeerId, NodeIdentifierWithPredicates> peerTables = HashMultimap.create();
     private volatile Map<PeerRole, PeerExportGroup> 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
index 5b827b4cb42c0813614137ffa899153f84555f47..be209349e7fad367be4e201831717b662afa15a0 100644 (file)
@@ -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<LocRibWriter> 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<DataTreeCandidate> 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<PeerId, YangInstanceIdentifier> 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 (file)
index 0000000..c095d35
--- /dev/null
@@ -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<TablesKey> 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 (file)
index 0000000..5dc113b
--- /dev/null
@@ -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<YangInstanceIdentifier> 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<Object>() {
+            @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<Object>() {
+            @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<Object>() {
+
+            @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());
+    }
+}
index 269e16ada5a2e43b38546ffd82608f8fe4df8172..61f0e70ad454051e146c45554593cc1d7835004c 100644 (file)
@@ -34,10 +34,33 @@ public final class RibSupportUtils {
      */
     public static NodeIdentifierWithPredicates toYangTablesKey(final Class<? extends AddressFamily> afi,
             final Class<? extends SubsequentAddressFamily> 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<? extends AddressFamily> afi, final Class<? extends SubsequentAddressFamily> safi) {
         final ImmutableMap<QName, Object> keyValues = ImmutableMap.<QName, Object>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());
     }
 
     /**
index ae7c4e78fd30b3f266bf0bdf6c63775d5d434a09..5e72780453ee6e064928c5188d414382b6c6046b 100644 (file)
@@ -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<? extends AddressFamily> afi = Ipv4AddressFamily.class;
+        final Class<? extends SubsequentAddressFamily> safi = UnicastSubsequentAddressFamily.class;
         final TablesKey k = new TablesKey(afi, safi);
         final NodeIdentifierWithPredicates p = RibSupportUtils.toYangTablesKey(k);
         final Map<QName, Object> 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<? extends AddressFamily> afi = Ipv4AddressFamily.class;
+        final Class<? extends SubsequentAddressFamily> safi = UnicastSubsequentAddressFamily.class;
+        final TablesKey k = new TablesKey(afi, safi);
+        final NodeIdentifierWithPredicates p = RibSupportUtils.toYangKey(SupportedTables.QNAME, k);
+        final Map<QName, Object> m = p.getKeyValues();
+        assertFalse(m.isEmpty());
+        assertEquals(SupportedTables.QNAME, p.getNodeType());
         assertTrue(m.containsValue(BindingReflections.findQName(afi)));
         assertTrue(m.containsValue(BindingReflections.findQName(safi)));
     }