BUG-2383: rework RIB inbound 80/15780/22
authorRobert Varga <rovarga@cisco.com>
Thu, 26 Feb 2015 14:18:39 +0000 (15:18 +0100)
committerDana Kutenicsova <dkutenic@cisco.com>
Thu, 5 Mar 2015 10:38:54 +0000 (10:38 +0000)
Each Peer (like BGPPeer) performs updates the adj-rib-in via a dedicated
AdjRibInWriter instance. That class, along with RIBSupport and TableContext,
is responsible for translating the Binding-Aware messages coming from
the message parser into Binding-Independent form within the datastore.

Changes are then propagated via DataChangeNotifications into
InboundPolicyEnforcer, which performs any munging of attributes required
and checks if the route is eligible for inclusion in the local RIB. If
it is, the route is output into the effective-rib-in hosted in the
datastore.

From there it is picked up by BestPathSelection, which compares the
effective routes from multiple peers and installs the best one into the
local RIB.

Change-Id: Ie23746b2fc44499f42d346595562b498c19fd112
Signed-off-by: Robert Varga <rovarga@cisco.com>
16 files changed:
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractIPRIBSupport.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AdjRibInWriter.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv4RIBSupport.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv6RIBSupport.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RIBSupportRegistry.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RouteEntry.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RouteUpdateKey.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/TableContext.java [new file with mode: 0644]
bgp/rib-spi/pom.xml
bgp/rib-spi/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/spi/AbstractRIBSupport.java [new file with mode: 0644]
bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java [new file with mode: 0644]
features/bgp/src/main/features/features.xml

diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java
new file mode 100644 (file)
index 0000000..a6402b1
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.collect.Maps;
+import java.util.EnumMap;
+import java.util.Map;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+/**
+ * Defines the internal hooks invoked when a new route appears.
+ */
+abstract class AbstractExportPolicy {
+    // Invoked on routes which we send outside of our home AS
+    private static final class ToExternalExportPolicy extends AbstractExportPolicy {
+        @Override
+        ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) {
+            // FIXME: filter all non-transitive attributes
+            // FIXME: but that may end up hurting our informedness
+            // FIXME: prepend local AS to add our AS into AS_PATH
+
+            switch (sourceRole) {
+            case Ebgp:
+                // eBGP -> eBGP, propagate
+                return attributes;
+            case Ibgp:
+                // Non-Client iBGP -> eBGP, propagate
+                return attributes;
+            case RrClient:
+                // Client iBGP -> eBGP, propagate
+                return attributes;
+            }
+            return attributes;
+        }
+    }
+
+    // Invoked on routes which we send to our normal home AS peers.
+    private static class ToInternalExportPolicy extends AbstractExportPolicy {
+        protected static ContainerNode prependClusterId(final ContainerNode attributes) {
+            // FIXME: prepend local CLUSTER_ID to CLUSTER_LIST
+            return attributes;
+        }
+
+        // Attributes when reflecting a route
+        protected static ContainerNode toClientAttributes(final ContainerNode attributes) {
+            // TODO: (defensiveness) verify ORIGINATOR_ID (should have been set)
+
+            return prependClusterId(attributes);
+        }
+
+        @Override
+        ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) {
+            // Implement filtering according to <a ref="http://tools.ietf.org/html/rfc4456#section-8"/>.
+
+            switch (sourceRole) {
+            case Ebgp:
+                // eBGP -> Non-Client iBGP, propagate
+                return attributes;
+
+            case Ibgp:
+                // Non-Client iBGP -> Non-Client iBGP, block
+                return null;
+
+            case RrClient:
+                // Client iBGP -> Non-Client iBGP, reflect
+                return toClientAttributes(attributes);
+            }
+            return attributes;
+        }
+    }
+
+    /**
+     * Invoked on routes which we send to our reflector peers. This is a special-case of
+     * FromInternalImportPolicy.
+     */
+    private static final class ToReflectorClientExportPolicy extends AbstractExportPolicy {
+        @Override
+        ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) {
+            switch (sourceRole) {
+            case Ebgp:
+                // eBGP -> Client iBGP, propagate
+                return attributes;
+            case Ibgp:
+                // Non-Client iBGP -> Client iBGP, reflect
+                // FIXME: add ORIGINATOR_ID
+
+                return ToInternalExportPolicy.prependClusterId(attributes);
+            case RrClient:
+                // Client iBGP -> Client iBGP, reflect
+                return ToInternalExportPolicy.toClientAttributes(attributes);
+            }
+
+            throw new IllegalStateException("Unhandled source role " + sourceRole);
+        }
+    }
+
+    static final Map<PeerRole, AbstractExportPolicy> POLICIES;
+
+    static {
+        final Map<PeerRole, AbstractExportPolicy> p = new EnumMap<PeerRole, AbstractExportPolicy>(PeerRole.class);
+        p.put(PeerRole.Ebgp, new ToExternalExportPolicy());
+        p.put(PeerRole.Ibgp, new ToInternalExportPolicy());
+        p.put(PeerRole.RrClient, new ToReflectorClientExportPolicy());
+        POLICIES = Maps.immutableEnumMap(p);
+    }
+
+    static AbstractExportPolicy forRole(final PeerRole peerRole) {
+        return POLICIES.get(peerRole);
+    }
+
+    /**
+     * Transform outgoing attributes according to policy.
+     *
+     * @param sourceRole role of the peer which originated the routes
+     * @param attributes outgoing attributes
+     * @return Filtered attributes, or null if the advertisement should be ignored.
+     */
+    abstract ContainerNode effectiveAttributes(PeerRole sourceRole, ContainerNode attributes);
+}
\ No newline at end of file
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractIPRIBSupport.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractIPRIBSupport.java
new file mode 100644 (file)
index 0000000..70c23a3
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+import org.opendaylight.controller.config.yang.bgp.rib.spi.AbstractRIBSupport;
+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.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.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Common {@link org.opendaylight.protocol.bgp.rib.spi.RIBSupport} class for IPv4 and IPv6 addresses.
+ */
+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 abstract NodeIdentifier routeIdentifier();
+    protected abstract NodeIdentifier routesIdentifier();
+
+    @Override
+    public final Collection<Class<? extends DataObject>> cacheableAttributeObjects() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public final Collection<Class<? extends DataObject>> cacheableNlriObjects() {
+        return Collections.emptySet();
+    }
+
+    private static enum ApplyRoute {
+        DELETE() {
+            @Override
+            void apply(final DOMDataWriteTransaction tx, final YangInstanceIdentifier base, final MapEntryNode route, final ContainerNode attributes) {
+                // FIXME: we need convert the namespace here, as per the comment below
+                tx.delete(LogicalDatastoreType.OPERATIONAL, base.node(route.getIdentifier()));
+            }
+        },
+        PUT() {
+            @Override
+            void apply(final DOMDataWriteTransaction tx, final YangInstanceIdentifier base, final MapEntryNode route, final ContainerNode attributes) {
+                /*
+                 * FIXME: We have a problem with namespaces, as the namespace defining the content
+                 *        in the message and the namespace defining the content in rib differ.
+                 *        Moving ipv4/ipv6 routes out into a separate model would solve the problem,
+                 *        but that's going to break our REST compatibility. Is there a generic
+                 *        solution to this problem?
+                 *
+                 *        Even if there is not, we need to transition to that separate model, so
+                 *        we get uniform and fast translation.
+                 */
+                // FIXME: use attributes
+                tx.put(LogicalDatastoreType.OPERATIONAL, base.node(route.getIdentifier()), route);
+            }
+        };
+
+        abstract void apply(DOMDataWriteTransaction tx, YangInstanceIdentifier base, MapEntryNode route, final ContainerNode attributes);
+    }
+
+    private final void processDestination(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId,
+            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());
+                    for (MapEntryNode e : ((MapNode)routes).getValue()) {
+                        function.apply(tx, base, e, attributes);
+                    }
+                } else {
+                    LOG.warn("Routes {} are not a map", routes);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void putDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode destination, final ContainerNode attributes) {
+        processDestination(tx, tableId, destination, attributes, ApplyRoute.PUT);
+    }
+
+    @Override
+    protected void deleteDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, final ContainerNode destination) {
+        processDestination(tx, tableId, destination, null, ApplyRoute.DELETE);
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java
new file mode 100644 (file)
index 0000000..d917450
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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 java.util.EnumMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+/**
+ * Defines the internal hooks invoked when a new route appears.
+ */
+abstract class AbstractImportPolicy {
+    // Invoked on routes which we get from outside of our home AS
+    private static final class FromExternalImportPolicy extends AbstractImportPolicy {
+        @Override
+        ContainerNode effectiveAttributes(final ContainerNode attributes) {
+            // FIXME: filter all non-transitive attributes
+            // FIXME: but that may end up hurting our informedness
+            return attributes;
+        }
+    }
+
+    // Invoked on routes which we get from our normal home AS peers.
+    private static class FromInternalImportPolicy extends AbstractImportPolicy {
+        @Override
+        ContainerNode effectiveAttributes(final ContainerNode attributes) {
+            // FIXME: Implement filtering according to <a href="http://tools.ietf.org/html/rfc4456#section-8"/>.
+            return attributes;
+        }
+    }
+
+    /**
+     * Invoked on routes which we get from our reflector peers. This is a special-case of
+     * FromInternalImportPolicy.
+     *
+     */
+    private static final class FromReflectorClientImportPolicy extends FromInternalImportPolicy {
+        // FIXME: override if necessary
+    }
+
+    private static final Map<PeerRole, AbstractImportPolicy> POLICIES;
+
+    static {
+        final Map<PeerRole, AbstractImportPolicy> p = new EnumMap<PeerRole, AbstractImportPolicy>(PeerRole.class);
+        p.put(PeerRole.Ebgp, new FromExternalImportPolicy());
+        p.put(PeerRole.Ibgp, new FromInternalImportPolicy());
+        p.put(PeerRole.RrClient, new FromReflectorClientImportPolicy());
+        POLICIES = p;
+    }
+
+    static AbstractImportPolicy forRole(final PeerRole peerRole) {
+        return POLICIES.get(peerRole);
+    }
+
+    /**
+     * Transform incoming attributes according to policy.
+     *
+     * @param attributes received attributes
+     * @return Filtered attributes, or null if the advertisement should be ignored.
+     */
+    abstract @Nullable ContainerNode effectiveAttributes(@Nullable ContainerNode attributes);
+}
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..5129b31
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.annotation.concurrent.NotThreadSafe;
+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.spi.RIBSupport;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
+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.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.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Writer of Adjacency-RIB-In for a single peer. An instance of this object
+ * is attached to each {@link BGPPeer} and {@link ApplicationPeer}.
+ */
+@NotThreadSafe
+final class AdjRibInWriter {
+    private static final Logger LOG = LoggerFactory.getLogger(AdjRibInWriter.class);
+
+    // FIXME: is there a utility method to construct this?
+    private static final ContainerNode EMPTY_ADJRIBIN = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(AdjRibIn.QNAME)).addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build()).build();
+    private 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 AFI_QNAME = QName.create(Tables.QNAME, "afi");
+    private static final QName SAFI_QNAME = QName.create(Tables.QNAME, "safi");
+
+    private final Map<TablesKey, TableContext> tables;
+    private final YangInstanceIdentifier adjRibInRoot;
+    private final DOMTransactionChain chain;
+
+    /*
+     * FIXME: transaction chain has to be instantiated in caller, so it can terminate us when it fails.
+     */
+    private AdjRibInWriter(final DOMTransactionChain chain, final YangInstanceIdentifier adjRibInRoot, final Map<TablesKey, TableContext> tables) {
+        this.chain = Preconditions.checkNotNull(chain);
+        this.adjRibInRoot = Preconditions.checkNotNull(adjRibInRoot);
+        this.tables = Preconditions.checkNotNull(tables);
+    }
+
+    static AdjRibInWriter create(final DOMTransactionChain chain, final YangInstanceIdentifier peer) {
+        // Not used often, no need to optimize it via builder
+        final YangInstanceIdentifier adjRibInRoot = peer.node(EMPTY_ADJRIBIN.getIdentifier());
+
+        // Create top-level AdjRibIn with an empty table list
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+        tx.put(LogicalDatastoreType.OPERATIONAL, adjRibInRoot, EMPTY_ADJRIBIN);
+        tx.submit();
+
+        return new AdjRibInWriter(chain, adjRibInRoot, Collections.<TablesKey, TableContext>emptyMap());
+    }
+
+    /**
+     * Transform this writer to a new writer, which is in charge of specified tables.
+     * Empty tables are created for new entries and old tables are deleted. Once this
+     * method returns, the old instance must not be reasonably used.
+     *
+     * @param tableTypes New tables, must not be null
+     * @return New writer
+     */
+    AdjRibInWriter changeTableTypes(final RIBSupportRegistry registry, final Set<TablesKey> tableTypes) {
+        if (tableTypes.equals(tables.keySet())) {
+            return this;
+        }
+
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+
+        // Wipe tables which are not present in the new types
+        for (Entry<TablesKey, TableContext> e : tables.entrySet()) {
+            if (!tableTypes.contains(e.getKey())) {
+                e.getValue().removeTable(tx);
+            }
+        }
+
+        final Builder<TablesKey, TableContext> tb = ImmutableMap.builder();
+        for (TablesKey k : tableTypes) {
+            TableContext ctx = tables.get(k);
+            if (ctx == null) {
+                final RIBSupport rs = registry.getRIBSupport(k);
+                if (rs == null) {
+                    LOG.warn("No support for table type {}, skipping it", k);
+                    continue;
+                }
+
+                // We will use table keys very often, make sure they are optimized
+                final InstanceIdentifierBuilder idb = YangInstanceIdentifier.builder(adjRibInRoot);
+
+                // FIXME: use codec to translate the key
+                final Map<QName, Object> keyValues = ImmutableMap.<QName, Object>of(AFI_QNAME, BindingReflections.getQName(k.getAfi()), SAFI_QNAME, BindingReflections.getQName(k.getSafi()));
+                final NodeIdentifierWithPredicates key = new NodeIdentifierWithPredicates(Tables.QNAME, keyValues);
+                idb.nodeWithKey(key.getNodeType(), keyValues);
+
+                ctx = new TableContext(rs, idb.build());
+                ctx.clearTable(tx);
+            } else {
+                tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_FALSE.getNodeType()), ATTRIBUTES_UPTODATE_FALSE);
+            }
+
+            tb.put(k, ctx);
+        }
+
+        tx.submit();
+
+        return new AdjRibInWriter(chain, adjRibInRoot, tb.build());
+    }
+
+    /**
+     * Clean all routes in specified tables
+     *
+     * @param tableTypes Tables to clean.
+     */
+    void cleanTables(final Collection<TablesKey> tableTypes) {
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+
+        for (TablesKey k : tableTypes) {
+            LOG.debug("Clearing table {}", k);
+            tables.get(k).clearTable(tx);
+        }
+
+        tx.submit();
+    }
+
+    void markTablesUptodate(final Collection<TablesKey> tableTypes) {
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+
+        for (TablesKey k : tableTypes) {
+            final TableContext ctx = tables.get(k);
+            tx.merge(LogicalDatastoreType.OPERATIONAL, ctx.getTableId().node(Attributes.QNAME).node(ATTRIBUTES_UPTODATE_TRUE.getNodeType()), ATTRIBUTES_UPTODATE_TRUE);
+        }
+
+        tx.submit();
+    }
+
+    void updateRoutes(final MpReachNlri nlri, final PathAttributes attributes) {
+        final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
+        final TableContext ctx = tables.get(key);
+        if (ctx == null) {
+            LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
+            return;
+        }
+
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+        ctx.writeRoutes(null, tx, nlri, attributes);
+        tx.submit();
+    }
+
+    void removeRoutes(final MpUnreachNlri nlri) {
+        final TablesKey key = new TablesKey(nlri.getAfi(), nlri.getSafi());
+        final TableContext ctx = tables.get(key);
+        if (ctx == null) {
+            LOG.debug("No table for {}, not accepting NLRI {}", key, nlri);
+            return;
+        }
+
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+        ctx.removeRoutes(null, tx, nlri);
+        tx.submit();
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
new file mode 100644 (file)
index 0000000..0d35700
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * 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.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+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;
+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.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.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.rib.Tables;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of the BGP import policy. Listens on all Adj-RIB-In, inspects all inbound
+ * routes in the context of the advertising peer's role and applies the inbound policy.
+ *
+ * Inbound policy is applied as follows:
+ *
+ * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
+ * 2) check if a route is admissible based on attributes attached to it, as well as the
+ *    advertising peer's role
+ * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
+ *
+ * Note that we maintain the peer roles using a DCL, even if we could look up our internal
+ * structures. This is done so we maintain causality and loose coupling.
+ */
+@NotThreadSafe
+final class EffectiveRibInWriter {
+    private static final Predicate<PathArgument> IS_PEER = new Predicate<PathArgument>() {
+        @Override
+        public boolean apply(final PathArgument input) {
+            return input.getNodeType().equals(Peer.QNAME);
+        }
+    };
+    private static final Predicate<PathArgument> IS_TABLES = new Predicate<PathArgument>() {
+        @Override
+        public boolean apply(final PathArgument input) {
+            return input.getNodeType().equals(Tables.QNAME);
+        }
+    };
+    private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
+    private static final QName PEER_ID = QName.create(Peer.QNAME, "peer-id");
+
+    // FIXME: implement as id.firstIdentifierOf(IS_PEER), null indicating not found
+    private static final NodeIdentifierWithPredicates firstKeyOf(final YangInstanceIdentifier id, final Predicate<PathArgument> match) {
+        final PathArgument ret = Iterables.find(id.getPathArguments(), IS_PEER);
+        Preconditions.checkArgument(ret instanceof NodeIdentifierWithPredicates, "Non-key peer identifier %s", ret);
+        return (NodeIdentifierWithPredicates) ret;
+    }
+
+    static final NodeIdentifierWithPredicates peerKey(final YangInstanceIdentifier id) {
+        return firstKeyOf(id, IS_PEER);
+    }
+
+    static final PeerId peerId(final NodeIdentifierWithPredicates peerKey) {
+        return (PeerId) peerKey.getKeyValues().get(PEER_ID);
+    }
+
+    private static final NodeIdentifierWithPredicates tableKey(final YangInstanceIdentifier id) {
+        return firstKeyOf(id, IS_TABLES);
+    }
+
+    /**
+     * Maintains the mapping of PeerId -> Role inside. We are subscribed to our target leaf,
+     * but that is a wildcard:
+     *     /bgp-rib/rib/peer/peer-role
+     *
+     * MD-SAL assumption: we are getting one {@link DataTreeCandidate} for each expanded
+     *                    wildcard path, so are searching for a particular key.
+     */
+    private final class PeerRoleListener implements DOMDataTreeChangeListener {
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+            synchronized (policies) {
+                for (DataTreeCandidate tc : changes) {
+                    // Obtain the peer's key
+                    final NodeIdentifierWithPredicates peerKey = peerKey(tc.getRootPath());
+
+                    // Check for removal
+                    final Optional<NormalizedNode<?, ?>> maybePeerRole = tc.getRootNode().getDataAfter();
+                    if (maybePeerRole.isPresent()) {
+                        final LeafNode<?> peerRoleLeaf = (LeafNode<?>) maybePeerRole.get();
+                        // FIXME: need codec here
+                        final PeerRole peerRole = (PeerRole) peerRoleLeaf.getValue();
+
+                        // Lookup policy based on role
+                        final AbstractImportPolicy policy = AbstractImportPolicy.forRole(peerRole);
+
+                        // Update lookup map
+                        policies.put(peerId(peerKey), policy);
+                    } else {
+                        policies.remove(peerId(peerKey));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Maintains the individual routes for a particular table's routes under:
+     *     /bgp-rib/rib/peer/adj-rib-in/tables/routes
+     */
+    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 = (PeerId) Preconditions.checkNotNull(peerKey.getKeyValues().get(PEER_ID));
+
+            // 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());
+
+            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);
+            }
+
+        }
+
+        @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) {
+                // Lookup per-table attributes from RIBSupport
+                final ContainerNode adverisedAttrs = (ContainerNode) NormalizedNodes.findNode(tc.getRootNode().getDataAfter(), ribSupport.routeAttributes()).orNull();
+                final ContainerNode effectiveAttrs;
+
+                if (adverisedAttrs != null && tc.getRootNode().getDataAfter().isPresent()) {
+                    synchronized (policies) {
+                        final AbstractImportPolicy policy = policies.get(peerId);
+                        effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
+                    }
+                } else {
+                    effectiveAttrs = null;
+                }
+
+                LOG.debug("Route change {} effective attributes {}", tc.getRootPath(), effectiveAttrs);
+
+                updateRoutes(tx, tc.getRootNode(), effectiveAttrs);
+            }
+
+            tx.submit();
+        }
+    }
+
+    /**
+     * Maintains {@link TableRouteListener} instances.
+     */
+    private final class TableListener implements DOMDataTreeChangeListener {
+        private final Map<YangInstanceIdentifier, ListenerRegistration<?>> routeListeners = new HashMap<>();
+        private final DOMDataTreeChangeService service;
+        private final RIBSupportRegistry registry;
+
+        TableListener(final DOMDataTreeChangeService service, final RIBSupportRegistry registry) {
+            this.registry = Preconditions.checkNotNull(registry);
+            this.service = Preconditions.checkNotNull(service);
+        }
+
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+
+            for (DataTreeCandidate tc : changes) {
+                // Obtain the peer's key
+                final NodeIdentifierWithPredicates peerKey = peerKey(tc.getRootPath());
+
+                // Lookup
+                final NodeIdentifierWithPredicates tableKey = tableKey(tc.getRootPath());
+
+                switch (tc.getRootNode().getModificationType()) {
+                case DELETE:
+                    final ListenerRegistration<?> reg = routeListeners.remove(tc.getRootPath());
+                    if (reg != null) {
+                        reg.close();
+                    }
+                    break;
+                case WRITE:
+                    // FIXME: use codec to translate or refactor registry
+                    final RIBSupport ribSupport = registry.getRIBSupport(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);
+                    break;
+                default:
+                    // No-op
+                    break;
+                }
+            }
+        }
+    }
+
+    private final Map<PeerId, AbstractImportPolicy> policies = new HashMap<>();
+    private final DOMTransactionChain chain;
+
+    private EffectiveRibInWriter(final DOMTransactionChain chain) {
+        this.chain = Preconditions.checkNotNull(chain);
+
+        // FIXME: subscribe peerRoleListener, tableListener
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv4RIBSupport.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv4RIBSupport.java
new file mode 100644 (file)
index 0000000..9dcf714
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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 org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.DestinationIpv4Case;
+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;
+
+final class IPv4RIBSupport extends AbstractIPRIBSupport {
+    private static final IPv4RIBSupport SINGLETON = new IPv4RIBSupport();
+    private final ChoiceNode emptyRoutes = Builders.choiceBuilder()
+            .withNodeIdentifier(new NodeIdentifier(Routes.QNAME))
+            .addChild(Builders.containerBuilder()
+                .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() {
+
+    }
+
+    static IPv4RIBSupport getInstance() {
+        return SINGLETON;
+    }
+
+    @Override
+    public ChoiceNode emptyRoutes() {
+        return emptyRoutes;
+    }
+
+    @Override
+    public NodeIdentifier routeAttributes() {
+        return attributes;
+    }
+
+    @Override
+    protected NodeIdentifier destinationIdentifier() {
+        return destination;
+    }
+
+    @Override
+    protected NodeIdentifier routeIdentifier() {
+        return route;
+    }
+
+    @Override
+    protected NodeIdentifier routesIdentifier() {
+        return routes;
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv6RIBSupport.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IPv6RIBSupport.java
new file mode 100644 (file)
index 0000000..ca61068
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.DestinationIpv6Case;
+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;
+
+final class IPv6RIBSupport extends AbstractIPRIBSupport {
+    private static final IPv6RIBSupport SINGLETON = new IPv6RIBSupport();
+    private final ChoiceNode emptyRoutes = Builders.choiceBuilder()
+            .withNodeIdentifier(new NodeIdentifier(Routes.QNAME))
+            .addChild(Builders.containerBuilder()
+                .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() {
+
+    }
+
+    static IPv6RIBSupport getInstance() {
+        return SINGLETON;
+    }
+
+    @Override
+    public ChoiceNode emptyRoutes() {
+        return emptyRoutes;
+    }
+
+    @Override
+    public NodeIdentifier routeAttributes() {
+        return attributes;
+    }
+
+    @Override
+    protected NodeIdentifier destinationIdentifier() {
+        return destination;
+    }
+
+    @Override
+    protected NodeIdentifier routeIdentifier() {
+        return route;
+    }
+
+    @Override
+    protected NodeIdentifier routesIdentifier() {
+        return routes;
+    }
+}
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
new file mode 100644 (file)
index 0000000..7f6c35f
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.Preconditions;
+import com.google.common.primitives.UnsignedInteger;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.annotation.concurrent.NotThreadSafe;
+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.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+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.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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+// FIXME: instantiate for each table, listen on wildcard peer and routes
+@NotThreadSafe
+final class LocRibWriter implements DOMDataTreeChangeListener {
+    private final Map<PathArgument, RouteEntry> routeEntries = new HashMap<>();
+    private final YangInstanceIdentifier target;
+    private final DOMTransactionChain chain;
+    private final RIBSupport ribSupport;
+    private final Long ourAs;
+
+    // FIXME: these maps need to be populated
+    private final Map<PeerRole, Map<PeerId, YangInstanceIdentifier>> peersToUpdate = new EnumMap<>(PeerRole.class);
+    private final Map<PeerId, PeerRole> peers = new HashMap<>();
+
+    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);
+    }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+        /*
+         * We use two-stage processing here in hopes that we avoid duplicate
+         * calculations when multiple peers have changed a particular entry.
+         */
+        final Map<RouteUpdateKey, RouteEntry> toUpdate = new HashMap<>();
+        for (DataTreeCandidate tc : changes) {
+            final YangInstanceIdentifier path = tc.getRootPath();
+            final PathArgument routeId = path.getLastPathArgument();
+            final NodeIdentifierWithPredicates peerKey = EffectiveRibInWriter.peerKey(path);
+            final PeerId peerId = EffectiveRibInWriter.peerId(peerKey);
+            final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId);
+
+            RouteEntry entry = routeEntries.get(routeId);
+            if (tc.getRootNode().getDataAfter().isPresent()) {
+                if (entry == null) {
+                    entry = new RouteEntry();
+                    routeEntries.put(routeId, entry);
+                }
+
+                entry.addRoute(routerId, (ContainerNode) tc.getRootNode().getDataAfter().get());
+            } else if (entry != null) {
+                if (entry.removeRoute(routerId)) {
+                    routeEntries.remove(routeId);
+                    entry = null;
+                }
+            }
+
+            toUpdate.put(new RouteUpdateKey(peerId, routeId), entry);
+        }
+
+        final DOMDataWriteTransaction tx = chain.newWriteOnlyTransaction();
+
+        // Now walk all updated entries
+        for (Entry<RouteUpdateKey, RouteEntry> e : toUpdate.entrySet()) {
+            final RouteEntry entry = e.getValue();
+            final NormalizedNode<?, ?> value;
+
+            if (entry != null) {
+                if (!entry.selectBest(ourAs)) {
+                    // Best path has not changed, no need to do anything else. Proceed to next route.
+                    continue;
+                }
+
+                value = entry.bestValue(e.getKey().getRouteId());
+            } else {
+                value = null;
+            }
+
+            if (value != null) {
+                tx.put(LogicalDatastoreType.OPERATIONAL, target.node(e.getKey().getRouteId()), value);
+            } else {
+                tx.delete(LogicalDatastoreType.OPERATIONAL, target.node(e.getKey().getRouteId()));
+            }
+
+            /*
+             * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
+             * expose from which client a particular route was learned from in the local RIB, and have
+             * the listener perform filtering.
+             *
+             * We walk the policy set in order to minimize the amount of work we do for multiple peers:
+             * if we have two eBGP peers, for example, there is no reason why we should perform the translation
+             * multiple times.
+             */
+            for (Entry<PeerRole, AbstractExportPolicy> pe : AbstractExportPolicy.POLICIES.entrySet()) {
+                final Map<PeerId, YangInstanceIdentifier> toPeers = peersToUpdate.get(pe.getKey());
+                if (toPeers == null || toPeers.isEmpty()) {
+                    continue;
+                }
+
+                final ContainerNode attributes = null;
+                final PeerId peerId = e.getKey().getPeerId();
+                final ContainerNode effectiveAttributes = pe.getValue().effectiveAttributes(peers.get(peerId), attributes);
+
+                for (Entry<PeerId, YangInstanceIdentifier> pid : toPeers.entrySet()) {
+                    // This points to adj-rib-out for a particlar peer/table combination
+                    final YangInstanceIdentifier routeTarget = pid.getValue().node(e.getKey().getRouteId());
+
+                    if (effectiveAttributes != null && value != null && !peerId.equals(pid.getKey())) {
+                        tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
+                        tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSupport.routeAttributes()), effectiveAttributes);
+                    } else {
+                        tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
+                    }
+                }
+            }
+        }
+
+        tx.submit();
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RIBSupportRegistry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RIBSupportRegistry.java
new file mode 100644 (file)
index 0000000..d6606b9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
+
+// FIXME: move to rib-spi/integrate with RIBExtension
+public interface RIBSupportRegistry {
+
+    @Nullable RIBSupport getRIBSupport(@Nonnull TablesKey k);
+
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RouteEntry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RouteEntry.java
new file mode 100644 (file)
index 0000000..7768a6b
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.primitives.UnsignedInteger;
+import java.util.Objects;
+import javax.annotation.concurrent.NotThreadSafe;
+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.NormalizedNode;
+
+/**
+ * A single route entry inside a route table. Maintains the attributes of
+ * from all contributing peers. The information is stored in arrays with a
+ * shared map of offsets for peers to allow lookups. This is needed to
+ * maintain low memory overhead in face of large number of routes and peers,
+ * where individual object overhead becomes the dominating factor.
+ */
+@NotThreadSafe
+final class RouteEntry {
+    private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
+
+    private OffsetMap offsets = OffsetMap.EMPTY;
+    private ContainerNode[] values = EMPTY_ATTRIBUTES;
+    private BestPath bestPath;
+
+    void addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
+        int offset = offsets.offsetOf(routerId);
+        if (offset < 0) {
+            final OffsetMap newOffsets = offsets.with(routerId);
+            offset = newOffsets.offsetOf(routerId);
+
+            final ContainerNode[] newAttributes = newOffsets.expand(offsets, this.values, offset);
+            this.values = newAttributes;
+            this.offsets = newOffsets;
+        }
+
+        offsets.setValue(this.values, offset, attributes);
+    }
+
+    // Indicates whether this was the last route
+    boolean removeRoute(final UnsignedInteger routerId) {
+        if (offsets.size() != 1) {
+            // FIXME: actually shrink the array
+            int offset = offsets.offsetOf(routerId);
+            offsets.setValue(values, offset, null);
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    // Indicates whether best has changed
+    boolean selectBest(final long localAs) {
+        /*
+         * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
+         */
+        final BestPathSelector selector = new BestPathSelector(localAs);
+
+        // Select the best route.
+        for (int i = 0; i < this.offsets.size(); ++i) {
+            final UnsignedInteger routerId = this.offsets.getRouterId(i);
+            final ContainerNode attributes = this.offsets.getValue(this.values, i);
+
+            selector.processPath(routerId, attributes);
+        }
+
+        // Get the newly-selected best path.
+        final BestPath newBestPath = selector.result();
+        // FIXME: run deeper comparison
+        final boolean ret = !Objects.equals(bestPath, newBestPath);
+
+        bestPath = newBestPath;
+        return ret;
+    }
+
+    NormalizedNode<?, ?> bestValue(final PathArgument key) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RouteUpdateKey.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RouteUpdateKey.java
new file mode 100644 (file)
index 0000000..f0c3a25
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.Preconditions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Combined key formed as a concatenation of source peer and route identifiers.
+ * This is used to internally track updates which need to be processed.
+ */
+final class RouteUpdateKey {
+    private final PeerId peerId;
+    private final PathArgument routeId;
+
+    RouteUpdateKey(final PeerId peerId, final PathArgument routeId) {
+        this.peerId = Preconditions.checkNotNull(peerId);
+        this.routeId = Preconditions.checkNotNull(routeId);
+    }
+
+    PeerId getPeerId() {
+        return peerId;
+    }
+
+    PathArgument getRouteId() {
+        return routeId;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + peerId.hashCode();
+        result = prime * result + routeId.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof RouteUpdateKey)) {
+            return false;
+        }
+        RouteUpdateKey other = (RouteUpdateKey) obj;
+        if (!peerId.equals(other.peerId)) {
+            return false;
+        }
+        if (!routeId.equals(other.routeId)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/TableContext.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/TableContext.java
new file mode 100644 (file)
index 0000000..2641429
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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.Preconditions;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import java.util.Set;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+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.message.rev130919.ClusterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.OriginatorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Aggregator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.AsPath;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Communities;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.ExtendedCommunities;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.LocalPref;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.MultiExitDisc;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Origin;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpAggregator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Community;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ExtendedCommunity;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+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.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+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.DataContainerNodeBuilder;
+
+/**
+ * A context for a single RIB table instance. It is always bound to a particular {@link AdjRibInWriter}.
+ *
+ * FIXME: need a better name once we local-rib and rib-out contexts
+ */
+@NotThreadSafe
+final class TableContext {
+    private static final ContainerNode EMPTY_TABLE_ATTRIBUTES = ImmutableNodes.containerNode(Attributes.QNAME);
+    private static final ContainerNode EMPTY_ROUTE_ATTRIBUTES = ImmutableNodes.containerNode(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.route.Attributes.QNAME);
+    private static final Set<Class<? extends DataObject>> ATTRIBUTE_CACHEABLES;
+
+    static {
+        final Builder<Class<? extends DataObject>> acb = ImmutableSet.builder();
+        acb.add(Aggregator.class);
+        acb.add(BgpAggregator.class);
+        acb.add(AsPath.class);
+        acb.add(ClusterId.class);
+        acb.add(Community.class);
+        acb.add(Communities.class);
+        acb.add(ExtendedCommunity.class);
+        acb.add(ExtendedCommunities.class);
+        acb.add(LocalPref.class);
+        acb.add(MultiExitDisc.class);
+        acb.add(Origin.class);
+        acb.add(OriginatorId.class);
+        ATTRIBUTE_CACHEABLES = acb.build();
+    }
+
+    private final YangInstanceIdentifier tableId;
+    private final RIBSupport tableSupport;
+    private final Object attributeCodec;
+    private final Object nlriCodec;
+
+    TableContext(final RIBSupport tableSupport, final YangInstanceIdentifier tableId) {
+        this.tableSupport = Preconditions.checkNotNull(tableSupport);
+        this.tableId = Preconditions.checkNotNull(tableId);
+
+        final Builder<Class<? extends DataObject>> acb = ImmutableSet.builder();
+        acb.addAll(ATTRIBUTE_CACHEABLES);
+        acb.addAll(tableSupport.cacheableAttributeObjects());
+
+        // FIXME: new Codec.create(acb.build(), tableSupport.cacheableNlriObjects());
+        attributeCodec = null;
+
+        // FIXME: new Codec.create(tableSupport.cacheableNlriObjects());
+        nlriCodec = null;
+    }
+
+    YangInstanceIdentifier getTableId() {
+        return tableId;
+    }
+
+    void clearTable(final DOMDataWriteTransaction tx) {
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> tb =
+                ImmutableNodes.mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates)tableId.getLastPathArgument()).withChild(EMPTY_TABLE_ATTRIBUTES);
+
+        final ChoiceNode routes = tableSupport.emptyRoutes();
+        Verify.verifyNotNull(routes, "Null empty routes in %s", this);
+        Verify.verify(Routes.QNAME.equals(routes.getNodeType()), "Empty routes have unexpected identifier %s, expected %s", routes.getNodeType(), Routes.QNAME);
+
+        tx.put(LogicalDatastoreType.OPERATIONAL, tableId, tb.withChild(routes).build());
+    }
+
+    void removeTable(final DOMDataWriteTransaction tx) {
+        tx.delete(LogicalDatastoreType.OPERATIONAL, tableId);
+    }
+
+    void writeRoutes(final Object codecFactory, final DOMDataWriteTransaction tx, final MpReachNlri nlri, final PathAttributes attributes) {
+
+        // FIXME: run the decoder process
+        final ContainerNode domNlri = (ContainerNode) nlriCodec;
+
+        // FIXME: run the decoder process
+        final ContainerNode domAttributes = (ContainerNode) attributeCodec;
+        final ContainerNode routeAttributes = Builders.containerBuilder(EMPTY_ROUTE_ATTRIBUTES).withValue(domAttributes.getValue()).build();
+
+        tableSupport.putRoutes(tx, tableId, domNlri, routeAttributes);
+    }
+
+    void removeRoutes(final Object object, final DOMDataWriteTransaction tx, final MpUnreachNlri nlri) {
+        // FIXME: run the decoder process
+        final ContainerNode domNlri = (ContainerNode) nlriCodec;
+
+        tableSupport.deleteRoutes(tx, tableId, domNlri);
+    }
+}
index 1f99ae3c46a086f6960dc1d9d256050956bb6dd0..c3d93541bc6da0a48cc46b1714efa99f5b1d0164 100644 (file)
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>protocol-framework</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-core-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
diff --git a/bgp/rib-spi/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/spi/AbstractRIBSupport.java b/bgp/rib-spi/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/spi/AbstractRIBSupport.java
new file mode 100644 (file)
index 0000000..4bc6ce2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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.controller.config.yang.bgp.rib.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public abstract class AbstractRIBSupport implements RIBSupport {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractRIBSupport.class);
+    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);
+
+    protected AbstractRIBSupport() {
+
+    }
+
+    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);
+
+    private static ContainerNode getDestination(final DataContainerChild<? extends PathArgument, ?> routes, final NodeIdentifier destinationId) {
+        if (routes instanceof ContainerNode) {
+            final Optional<DataContainerChild<? extends PathArgument, ?>> maybeDestination = ((ContainerNode)routes).getChild(DESTINATION_TYPE);
+            if (maybeDestination.isPresent()) {
+                final DataContainerChild<? extends PathArgument, ?> destination = maybeDestination.get();
+                if (destination instanceof ChoiceNode) {
+                    final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRet = ((ChoiceNode)destination).getChild(destinationId);
+                    if (maybeRet.isPresent()) {
+                        final DataContainerChild<? extends PathArgument, ?> ret = maybeRet.get();
+                        if (ret instanceof ContainerNode) {
+                            return (ContainerNode)ret;
+                        } else {
+                            LOG.debug("Specified node {} is not a container, ignoring it", ret);
+                        }
+                    } else {
+                        LOG.debug("Specified container {} is not present in destination {}", destinationId, destination);
+                    }
+                } else {
+                    LOG.warn("Destination {} is not a choice, ignoring it", destination);
+                }
+            } else {
+                LOG.debug("Destination is not present in routes {}", routes);
+            }
+        } else {
+            LOG.warn("Advertized routes {} are not a container, ignoring it", routes);
+        }
+
+        return null;
+    }
+
+    @Override
+    public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, 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);
+            }
+        } else {
+            LOG.debug("Withdrawn routes are not present in NLRI {}", nlri);
+        }
+    }
+
+    @Override
+    public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tableId, 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);
+            }
+        } else {
+            LOG.debug("Advertized routes are not present in NLRI {}", nlri);
+        }
+    }
+}
diff --git a/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java b/bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/RIBSupport.java
new file mode 100644 (file)
index 0000000..fea3928
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.spi;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+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.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+public interface RIBSupport {
+    /**
+     * Return the table-type-specific empty routes container, as augmented into the
+     * bgp-rib model under /rib/tables/routes choice node. This needs to include all
+     * the skeleton nodes under which the individual routes will be stored.
+     *
+     * @return Protocol-specific case in the routes choice, may not be null.
+     */
+    @Nonnull ChoiceNode emptyRoutes();
+    @Nonnull NodeIdentifier routeAttributes();
+
+    @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);
+
+}
index 902869146da685ad030e1da994f84562d652fde3..7ab1830980dccb6e6b4976750aee0860eca097d5 100644 (file)
@@ -54,6 +54,7 @@
         <bundle>mvn:org.opendaylight.bgpcep/bgp-parser-api/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.bgpcep/bgp-concepts/{{VERSION}}</bundle>
         <feature version='${protocol-framework.version}'>odl-protocol-framework</feature>
+        <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
         <bundle>mvn:org.opendaylight.bgpcep/bgp-rib-api/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.bgpcep/bgp-rib-spi/{{VERSION}}</bundle>
     </feature>