--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
<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>
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+}
<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>