BUG-2383: implement EffectiveRibInWriter 17/16217/5
authorRobert Varga <rovarga@cisco.com>
Mon, 9 Mar 2015 18:25:17 +0000 (19:25 +0100)
committerRobert Varga <rovarga@cisco.com>
Mon, 9 Mar 2015 22:06:58 +0000 (23:06 +0100)
This completes the basic structure of EffectiveRibInWriter, such that
its flows are stable API-wise.

Change-Id: Idebad5a1d05667cdd767d16dc9278574dba1d3f6
Signed-off-by: Robert Varga <rovarga@cisco.com>
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractIPRIBSupport.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv4RIBSupport.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv6RIBSupport.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java
bgp/rib-spi/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/spi/AbstractRIBSupport.java
bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java

index 70c23a3291b5ecd307d8a6be8a5e1f41fb497e20..96a6741b8143491e71a7eaee9fc1a6b2723adcd5 100644 (file)
@@ -15,6 +15,7 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+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.PathArgument;
@@ -32,12 +33,11 @@ abstract class AbstractIPRIBSupport extends AbstractRIBSupport {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractIPRIBSupport.class);
     private static final NodeIdentifier ROUTES = new NodeIdentifier(Routes.QNAME);
 
-    protected AbstractIPRIBSupport() {
-
+    protected AbstractIPRIBSupport(final QName routesContainer) {
+        super(routesContainer);
     }
 
     protected abstract NodeIdentifier routeIdentifier();
-    protected abstract NodeIdentifier routesIdentifier();
 
     @Override
     public final Collection<Class<? extends DataObject>> cacheableAttributeObjects() {
@@ -78,14 +78,14 @@ abstract class AbstractIPRIBSupport extends AbstractRIBSupport {
         abstract void apply(DOMDataWriteTransaction tx, YangInstanceIdentifier base, MapEntryNode route, final ContainerNode attributes);
     }
 
-    private final void processDestination(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId,
+    private final void processDestination(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
             final ContainerNode destination, final ContainerNode attributes, final ApplyRoute function) {
         if (destination != null) {
             final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = destination.getChild(routeIdentifier());
             if (maybeRoutes.isPresent()) {
                 final DataContainerChild<? extends PathArgument, ?> routes = maybeRoutes.get();
                 if (routes instanceof MapNode) {
-                    final YangInstanceIdentifier base = tableId.node(ROUTES).node(routesIdentifier());
+                    final YangInstanceIdentifier base = tablePath.node(ROUTES).node(routesContainerIdentifier());
                     for (MapEntryNode e : ((MapNode)routes).getValue()) {
                         function.apply(tx, base, e, attributes);
                     }
@@ -97,12 +97,12 @@ abstract class AbstractIPRIBSupport extends AbstractRIBSupport {
     }
 
     @Override
-    protected void putDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode destination, final ContainerNode attributes) {
-        processDestination(tx, tableId, destination, attributes, ApplyRoute.PUT);
+    protected void putDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode destination, final ContainerNode attributes) {
+        processDestination(tx, tablePath, destination, attributes, ApplyRoute.PUT);
     }
 
     @Override
-    protected void deleteDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode destination) {
-        processDestination(tx, tableId, destination, null, ApplyRoute.DELETE);
+    protected void deleteDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode destination) {
+        processDestination(tx, tablePath, destination, null, ApplyRoute.DELETE);
     }
 }
index aff22eb37eab9efa55d215accf02e503f11ab21e..79cbf77d8491b716cebd97dab0f72e46579b2c30 100644 (file)
@@ -8,9 +8,8 @@
 package org.opendaylight.protocol.bgp.rib.impl;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
 import javax.annotation.concurrent.NotThreadSafe;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
@@ -20,10 +19,16 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
-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.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.graceful.restart._case.graceful.restart.capability.Tables;
+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.EffectiveRibIn;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 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.NormalizedNodes;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
@@ -47,133 +52,204 @@ import org.slf4j.LoggerFactory;
  */
 @NotThreadSafe
 final class EffectiveRibInWriter implements AutoCloseable {
-
     private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
+    private static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
 
     /**
-     * Maintains the individual routes for a particular table's routes under:
-     *     /bgp-rib/rib/peer/adj-rib-in/tables/routes
+     * Maintains {@link TableRouteListener} instances.
      */
-    private final class TableRouteListener implements DOMDataTreeChangeListener {
-        private final NodeIdentifierWithPredicates tableKey;
-        private final YangInstanceIdentifier target;
-        private final RIBSupport ribSupport;
-        private final PeerId peerId;
-
-        TableRouteListener(final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
-            this.ribSupport = Preconditions.checkNotNull(ribSupport);
-            this.tableKey = Preconditions.checkNotNull(tableKey);
-
-            // Lookup peer ID
-            this.peerId = IdentifierUtils.peerId(peerKey);
-
-            // FIXME: need target table ID
-            target = null;
-        }
-
-        private void updateRoutes(final DOMDataWriteTransaction tx, final DataTreeCandidateNode routes, final ContainerNode effectiveAttrs) {
-            final YangInstanceIdentifier routeId = target.node(routes.getIdentifier());
+    private final class AdjInTracker implements AutoCloseable, DOMDataTreeChangeListener {
+        private final RIBExtensionConsumerContext registry;
+        private final YangInstanceIdentifier ribId;
+        private final ListenerRegistration<?> reg;
+        private final DOMTransactionChain chain;
 
-            if (effectiveAttrs != null) {
-                tx.put(LogicalDatastoreType.OPERATIONAL, routeId, routes.getDataAfter().get());
-                tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributes()), effectiveAttrs);
-            } else if (routes.getDataBefore().isPresent()) {
-                tx.delete(LogicalDatastoreType.OPERATIONAL, routeId);
-            }
+        AdjInTracker(final DOMDataTreeChangeService service, final RIBExtensionConsumerContext registry, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
+            this.registry = Preconditions.checkNotNull(registry);
+            this.chain = Preconditions.checkNotNull(chain);
+            this.ribId = Preconditions.checkNotNull(ribId);
 
+            final YangInstanceIdentifier tableId = ribId.node(Peer.QNAME).node(AdjRibIn.QNAME).node(Tables.QNAME);
+            final DOMDataTreeIdentifier treeId = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId);
+            this.reg = service.registerDataTreeChangeListener(treeId, this);
         }
 
-        @Override
-        public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
-            // FIXME: note that we need to detect table clears efficiently and propagate them
-
-            final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
-
-            for (DataTreeCandidate tc : changes) {
+        private void processRoute(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier routesPath, final DataTreeCandidateNode route) {
+            switch (route.getModificationType()) {
+            case DELETE:
+                // Delete has already been affected by the store in caller, so this is a no-op.
+                break;
+            case MERGE:
+                LOG.info("Merge on {} reported, this should never have happened, ignoring", route);
+                break;
+            case UNMODIFIED:
+                // No-op
+                break;
+            case SUBTREE_MODIFIED:
+            case WRITE:
                 // Lookup per-table attributes from RIBSupport
-                final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(tc.getRootNode().getDataAfter(), ribSupport.routeAttributes()).orNull();
+                final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(route.getDataAfter(), ribSupport.routeAttributesIdentifier()).orNull();
                 final ContainerNode effectiveAttrs;
 
-                if (adverisedAttrs != null && tc.getRootNode().getDataAfter().isPresent()) {
-                    final AbstractImportPolicy policy = peerPolicyTracker.policyFor(peerId);
+                if (adverisedAttrs != null) {
                     effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
+
+                    /*
+                     * Speed hack: if we determine that the policy has passed the attributes
+                     * back unmodified, the corresponding change has already been written in
+                     * our caller. There is no need to perform any further processing.
+                     *
+                     * We also use direct object comparison to make the check very fast, as
+                     * it may not be that common, in which case it does not make sense to pay
+                     * the full equals price.
+                     */
+                    if (effectiveAttrs == adverisedAttrs) {
+                        return;
+                    }
                 } else {
                     effectiveAttrs = null;
                 }
 
-                LOG.debug("Route change {} effective attributes {}", tc.getRootPath(), effectiveAttrs);
+                final YangInstanceIdentifier routeId = ribSupport.routePath(routesPath, route.getIdentifier());
+                LOG.debug("Route {} effective attributes {} towards {}", route.getIdentifier(), effectiveAttrs, routeId);
 
-                updateRoutes(tx, tc.getRootNode(), effectiveAttrs);
+                if (effectiveAttrs != null) {
+                    tx.put(LogicalDatastoreType.OPERATIONAL, routeId.node(ribSupport.routeAttributesIdentifier()), effectiveAttrs);
+                } else {
+                    LOG.warn("Route {} advertised empty attributes", route.getDataAfter());
+                    tx.delete(LogicalDatastoreType.OPERATIONAL,  routeId);
+                }
+                break;
+            default:
+                LOG.warn("Ignoring unhandled route {}", route);
+                break;
             }
+        }
 
-            tx.submit();
+        private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
+            for (DataTreeCandidateNode child : children) {
+                switch (child.getModificationType()) {
+                case DELETE:
+                    tx.delete(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()));
+                    break;
+                case MERGE:
+                    LOG.info("Merge on {} reported, this should never have happened, ignoring", child);
+                    break;
+                case UNMODIFIED:
+                    // No-op
+                    break;
+                case SUBTREE_MODIFIED:
+                case WRITE:
+                    tx.put(LogicalDatastoreType.OPERATIONAL, tablePath.node(child.getIdentifier()), child.getDataAfter().get());
+
+                    // Routes are special, as they may end up being filtered. The previous put conveniently
+                    // ensured that we have them in at target, so a subsequent delete will not fail :)
+                    if (TABLE_ROUTES.equals(child.getIdentifier())) {
+                        final YangInstanceIdentifier routesPath = tablePath.node(Routes.QNAME);
+                        for (DataTreeCandidateNode route : ribSupport.changedRoutes(child)) {
+                            processRoute(tx, ribSupport, policy, routesPath, route);
+                        }
+                    }
+                    break;
+                default:
+                    LOG.warn("Ignoring unhandled child {}", child);
+                    break;
+                }
+            }
         }
-    }
 
-    /**
-     * Maintains {@link TableRouteListener} instances.
-     */
-    private final class TableListener implements DOMDataTreeChangeListener {
-        private final Map<YangInstanceIdentifier, ListenerRegistration<?>> routeListeners = new HashMap<>();
-        private final RIBExtensionConsumerContext registry;
-        private final DOMDataTreeChangeService service;
+        private RIBSupport getRibSupport(final NodeIdentifierWithPredicates tableKey) {
+            // FIXME: use codec to translate tableKey
+            return registry.getRIBSupport(null);
+        }
 
-        TableListener(final DOMDataTreeChangeService service, final RIBExtensionConsumerContext registry) {
-            this.registry = Preconditions.checkNotNull(registry);
-            this.service = Preconditions.checkNotNull(service);
+        private YangInstanceIdentifier effectiveTablePath(final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey) {
+            return ribId.node(peerKey).node(EffectiveRibIn.QNAME).node(tableKey);
+        }
+
+        private void modifyTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
+            final RIBSupport ribSupport = getRibSupport(tableKey);
+            final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
+
+            final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
+            processTableChildren(tx, ribSupport, policy, tablePath, table.getChildNodes());
+        }
+
+        private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) {
+            final RIBSupport ribSupport = getRibSupport(tableKey);
+            final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey);
+
+            // Create an empty table
+            TableContext.clearTable(tx, ribSupport, tablePath);
+
+            final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
+            processTableChildren(tx, ribSupport, policy, tablePath, table.getChildNodes());
         }
 
         @Override
         public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+            final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
 
             for (DataTreeCandidate tc : changes) {
+                final YangInstanceIdentifier rootPath = tc.getRootPath();
+
                 // Obtain the peer's key
-                final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(tc.getRootPath());
+                final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(rootPath);
 
-                // Lookup
-                final NodeIdentifierWithPredicates tableKey = IdentifierUtils.tableKey(tc.getRootPath());
+                // Extract the table key, this should be safe based on the path where we subscribed,
+                // but let's verify explicitly.
+                final PathArgument lastArg = rootPath.getLastPathArgument();
+                Verify.verify(lastArg instanceof NodeIdentifierWithPredicates, "Unexpected type %s in path %s", lastArg.getClass(), rootPath);
+                final NodeIdentifierWithPredicates tableKey = (NodeIdentifierWithPredicates) lastArg;
 
-                switch (tc.getRootNode().getModificationType()) {
+                final DataTreeCandidateNode root = tc.getRootNode();
+                switch (root.getModificationType()) {
                 case DELETE:
-                    final ListenerRegistration<?> reg = routeListeners.remove(tc.getRootPath());
-                    if (reg != null) {
-                        reg.close();
-                    }
+                    // delete the corresponding effective table
+                    tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath(peerKey, tableKey));
+                    break;
+                case MERGE:
+                    // TODO: upstream API should never give us this, as it leaks how the delta was created.
+                    LOG.info("Merge on {} reported, this should never have happened, but attempting to cope", rootPath);
+                    modifyTable(tx, peerKey, tableKey, root);
+                    break;
+                case SUBTREE_MODIFIED:
+                    modifyTable(tx, peerKey, tableKey, root);
+                    break;
+                case UNMODIFIED:
+                    LOG.info("Ignoring spurious notification on {} data {}", rootPath, root);
                     break;
                 case WRITE:
-                    // FIXME: use codec to translate
-                    final RIBSupport ribSupport = registry.getRIBSupport(null);
-                    if (ribSupport != null) {
-                        final TableRouteListener routeListener = new TableRouteListener(ribSupport, peerKey, tableKey);
-                        final ListenerRegistration<?> r = service.registerDataTreeChangeListener(
-                            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,  tc.getRootPath()), routeListener);
-
-                        routeListeners.put(tc.getRootPath(), r);
-                    } else {
-                        LOG.warn("No RIB support for table {}, ignoring advertisements from peer %s", tableKey, peerKey);
-                    }
+                    writeTable(tx, peerKey, tableKey, root);
                     break;
                 default:
-                    // No-op
+                    LOG.warn("Ignoring unhandled root {}", root);
                     break;
                 }
             }
+
+            tx.submit();
+        }
+
+        @Override
+        public void close() {
+            // FIXME: wipe all effective routes?
+            reg.close();
         }
     }
 
     private final ImportPolicyPeerTracker peerPolicyTracker;
-    private final DOMTransactionChain chain;
+    private final AdjInTracker adjInTracker;
 
     private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
-        this.chain = Preconditions.checkNotNull(chain);
-
         this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId);
-
-        // FIXME: subscribe tableListener
+        // FIXME: proper argument
+        this.adjInTracker = new AdjInTracker(service, null, chain, ribId);
     }
 
     @Override
     public void close() {
+        adjInTracker.close();
         peerPolicyTracker.close();
     }
 }
index 9dcf714e3b94e585d77495a0ca8ec6e71f17e90b..853b42997f7e57454f74d1853a4c67097e019319 100644 (file)
@@ -11,13 +11,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.mult
 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.rib.rev130925.rib.tables.routes.ipv4.routes._case.Ipv4Routes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.ipv4.routes._case.ipv4.routes.Ipv4Route;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 
+/**
+ * Class supporting IPv4 unicast RIBs.
+ */
 final class IPv4RIBSupport extends AbstractIPRIBSupport {
     private static final IPv4RIBSupport SINGLETON = new IPv4RIBSupport();
     private final ChoiceNode emptyRoutes = Builders.choiceBuilder()
@@ -26,14 +27,10 @@ final class IPv4RIBSupport extends AbstractIPRIBSupport {
                 .withNodeIdentifier(new NodeIdentifier(Ipv4Routes.QNAME))
                 .withChild(ImmutableNodes.mapNodeBuilder(Ipv4Route.QNAME).build()).build()).build();
     private final NodeIdentifier destination = new NodeIdentifier(DestinationIpv4Case.QNAME);
-    private final NodeIdentifier routes = new NodeIdentifier(Ipv4Routes.QNAME);
     private final NodeIdentifier route = new NodeIdentifier(Ipv4Route.QNAME);
-    private final NodeIdentifier attributes = new NodeIdentifier(QName.create(Ipv4Route.QNAME, Attributes.QNAME.getLocalName()));
-
-    private static final QName PREFIX_QNAME = QName.create(Ipv4Route.QNAME, "prefix");
 
     private IPv4RIBSupport() {
-
+        super(Ipv4Routes.QNAME);
     }
 
     static IPv4RIBSupport getInstance() {
@@ -45,11 +42,6 @@ final class IPv4RIBSupport extends AbstractIPRIBSupport {
         return emptyRoutes;
     }
 
-    @Override
-    public NodeIdentifier routeAttributes() {
-        return attributes;
-    }
-
     @Override
     protected NodeIdentifier destinationIdentifier() {
         return destination;
@@ -59,9 +51,4 @@ final class IPv4RIBSupport extends AbstractIPRIBSupport {
     protected NodeIdentifier routeIdentifier() {
         return route;
     }
-
-    @Override
-    protected NodeIdentifier routesIdentifier() {
-        return routes;
-    }
 }
index ca6106885df5ec50a6f6221c2741f7dc8287bab0..67be441b52b528e2ea8cc968d880be5d3e7ec438 100644 (file)
@@ -11,13 +11,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.mult
 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.rib.rev130925.rib.tables.routes.ipv6.routes._case.Ipv6Routes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.ipv6.routes._case.ipv6.routes.Ipv6Route;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 
+/**
+ * Class supporting IPv6 unicast RIBs.
+ */
 final class IPv6RIBSupport extends AbstractIPRIBSupport {
     private static final IPv6RIBSupport SINGLETON = new IPv6RIBSupport();
     private final ChoiceNode emptyRoutes = Builders.choiceBuilder()
@@ -26,12 +27,10 @@ final class IPv6RIBSupport extends AbstractIPRIBSupport {
                 .withNodeIdentifier(new NodeIdentifier(Ipv6Routes.QNAME))
                 .withChild(ImmutableNodes.mapNodeBuilder(Ipv6Route.QNAME).build()).build()).build();
     private final NodeIdentifier destination = new NodeIdentifier(DestinationIpv6Case.QNAME);
-    private final NodeIdentifier routes = new NodeIdentifier(Ipv6Routes.QNAME);
     private final NodeIdentifier route = new NodeIdentifier(Ipv6Route.QNAME);
-    private final NodeIdentifier attributes = new NodeIdentifier(QName.create(Ipv6Route.QNAME, Attributes.QNAME.getLocalName()));
 
     private IPv6RIBSupport() {
-
+        super(Ipv6Routes.QNAME);
     }
 
     static IPv6RIBSupport getInstance() {
@@ -43,11 +42,6 @@ final class IPv6RIBSupport extends AbstractIPRIBSupport {
         return emptyRoutes;
     }
 
-    @Override
-    public NodeIdentifier routeAttributes() {
-        return attributes;
-    }
-
     @Override
     protected NodeIdentifier destinationIdentifier() {
         return destination;
@@ -57,9 +51,4 @@ final class IPv6RIBSupport extends AbstractIPRIBSupport {
     protected NodeIdentifier routeIdentifier() {
         return route;
     }
-
-    @Override
-    protected NodeIdentifier routesIdentifier() {
-        return routes;
-    }
 }
index 9f8ce9627c259a27046b1d7d4db78adb8b60757c..2628b23bf857471936f7998e766684ee00292a26 100644 (file)
@@ -22,6 +22,7 @@ import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
 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.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;
@@ -35,14 +36,14 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     private final YangInstanceIdentifier target;
     private final DOMTransactionChain chain;
     private final ExportPolicyPeerTracker peerPolicyTracker;
-    private final RIBSupport ribSupport;
+    private final NodeIdentifier attributesIdentifier;
     private final Long ourAs;
 
     LocRibWriter(final RIBSupport ribSupport, final DOMTransactionChain chain, final YangInstanceIdentifier target, final Long ourAs) {
         this.chain = Preconditions.checkNotNull(chain);
         this.target = Preconditions.checkNotNull(target);
         this.ourAs = Preconditions.checkNotNull(ourAs);
-        this.ribSupport = Preconditions.checkNotNull(ribSupport);
+        this.attributesIdentifier = ribSupport.routeAttributesIdentifier();
 
         // FIXME: proper values
         this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null);
@@ -131,7 +132,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
 
                         if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) {
                             tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
-                            tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSupport.routeAttributes()), effectiveAttributes);
+                            tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(attributesIdentifier), effectiveAttributes);
                         } else {
                             tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
                         }
index 4bc6ce2c3c6a9a7edbaf5512482bd779e1a70352..245f63336fd51ad71a76d42c2ee6f2da572459c3 100644 (file)
@@ -9,17 +9,23 @@ package org.opendaylight.controller.config.yang.bgp.rib.spi;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes;
+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.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,14 +35,35 @@ public abstract class AbstractRIBSupport implements RIBSupport {
     private static final NodeIdentifier ADVERTIZED_ROUTES = new NodeIdentifier(AdvertizedRoutes.QNAME);
     private static final NodeIdentifier WITHDRAWN_ROUTES = new NodeIdentifier(WithdrawnRoutes.QNAME);
     private static final NodeIdentifier DESTINATION_TYPE = new NodeIdentifier(DestinationType.QNAME);
+    private final NodeIdentifier routesContainerIdentifier;
+    private final NodeIdentifier routeAttributesIdentifier;
 
-    protected AbstractRIBSupport() {
+    /**
+     * Default constructor. Requires the QName of the container augmented under the routes choice
+     * node in instantiations of the rib grouping. It is assumed that this container is defined by
+     * the same model which populates it with route grouping instantiation, and by extension with
+     * the route attributes container.
+     *
+     * @param routesContainer QName of the container in routes choice, must not be null.
+     */
+    protected AbstractRIBSupport(final @Nonnull QName routesContainer) {
+        this.routesContainerIdentifier = new NodeIdentifier(routesContainer);
+        this.routeAttributesIdentifier = new NodeIdentifier(QName.cachedReference(QName.create(routesContainer, Attributes.QNAME.getLocalName())));
+    }
 
+    /**
+     * Return the {@link NodeIdentifier} of the AFI/SAFI-specific container under
+     * the RIB routes.
+     *
+     * @return Container identifier, may not be null.
+     */
+    protected final NodeIdentifier routesContainerIdentifier() {
+        return routesContainerIdentifier;
     }
 
     protected abstract NodeIdentifier destinationIdentifier();
-    protected abstract void deleteDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tableId, ContainerNode destination);
-    protected abstract void putDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tableId, ContainerNode destination, ContainerNode attributes);
+    protected abstract void deleteDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination);
+    protected abstract void putDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination, ContainerNode attributes);
 
     private static ContainerNode getDestination(final DataContainerChild<? extends PathArgument, ?> routes, final NodeIdentifier destinationId) {
         if (routes instanceof ContainerNode) {
@@ -69,12 +96,34 @@ public abstract class AbstractRIBSupport implements RIBSupport {
     }
 
     @Override
-    public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode nlri) {
+    public final NodeIdentifier routeAttributesIdentifier() {
+        return routeAttributesIdentifier;
+    }
+
+    @Override
+    public final Collection<DataTreeCandidateNode> changedRoutes(final DataTreeCandidateNode routes) {
+        final DataTreeCandidateNode myRoutes = routes.getModifiedChild(routesContainerIdentifier);
+        if (myRoutes == null) {
+            return Collections.emptySet();
+        }
+
+        // Well, given the remote possibility of augmentation, we should perform a filter here,
+        // to make sure the type matches what routeType() reports.
+        return myRoutes.getChildNodes();
+    }
+
+    @Override
+    public final YangInstanceIdentifier routePath(final YangInstanceIdentifier routesPath, final PathArgument routeId) {
+        return routesPath.node(routesContainerIdentifier).node(routeId);
+    }
+
+    @Override
+    public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri) {
         final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(WITHDRAWN_ROUTES);
         if (maybeRoutes.isPresent()) {
             final ContainerNode destination = getDestination(maybeRoutes.get(), destinationIdentifier());
             if (destination != null) {
-                deleteDestinationRoutes(tx, tableId, destination);
+                deleteDestinationRoutes(tx, tablePath, destination);
             }
         } else {
             LOG.debug("Withdrawn routes are not present in NLRI {}", nlri);
@@ -82,12 +131,12 @@ public abstract class AbstractRIBSupport implements RIBSupport {
     }
 
     @Override
-    public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode nlri, final ContainerNode attributes) {
+    public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri, final ContainerNode attributes) {
         final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(ADVERTIZED_ROUTES);
         if (maybeRoutes.isPresent()) {
             final ContainerNode destination = getDestination(maybeRoutes.get(), destinationIdentifier());
             if (destination != null) {
-                putDestinationRoutes(tx, tableId, destination, attributes);
+                putDestinationRoutes(tx, tablePath, destination, attributes);
             }
         } else {
             LOG.debug("Advertized routes are not present in NLRI {}", nlri);
index fea392859ff78e11922d0ae1e369419c95f053a4..7b304765d765a64003a782899dd430fb4ee7a65e 100644 (file)
@@ -13,9 +13,16 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 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.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
 
+/**
+ * Interface implemented for AFI/SAFI-specific RIB extensions. The extensions need
+ * to register an implementation of this class and the RIB core then calls into it
+ * to inquire about details specific to that particular model.
+ */
 public interface RIBSupport {
     /**
      * Return the table-type-specific empty routes container, as augmented into the
@@ -25,11 +32,20 @@ public interface RIBSupport {
      * @return Protocol-specific case in the routes choice, may not be null.
      */
     @Nonnull ChoiceNode emptyRoutes();
-    @Nonnull NodeIdentifier routeAttributes();
+
+    /**
+     * Return the localized identifier of the attributes route member, as expanded
+     * from the route grouping in the specific augmentation of the base routes choice.
+     *
+     * @return The attributes identifier, may not be null.
+     */
+    @Nonnull NodeIdentifier routeAttributesIdentifier();
 
     @Nonnull Collection<Class<? extends DataObject>> cacheableAttributeObjects();
     @Nonnull Collection<Class<? extends DataObject>> cacheableNlriObjects();
-    void deleteRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tableId, @Nonnull ContainerNode nlri);
-    void putRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tableId, @Nonnull ContainerNode nlri, @Nonnull ContainerNode attributes);
+    void deleteRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tablePath, @Nonnull ContainerNode nlri);
+    void putRoutes(@Nonnull DOMDataWriteTransaction tx, @Nonnull YangInstanceIdentifier tablePath, @Nonnull ContainerNode nlri, @Nonnull ContainerNode attributes);
 
+    @Nonnull Collection<DataTreeCandidateNode> changedRoutes(@Nonnull DataTreeCandidateNode routes);
+    @Nonnull YangInstanceIdentifier routePath(@Nonnull YangInstanceIdentifier routesPath, @Nonnull PathArgument routeId);
 }