<artifactId>bgp-util</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-path-selection-mode</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-path-selection-mode</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>bgp-controller-config</artifactId>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>bgp-path-selection-mode</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 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
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/bgpcep.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/bgpcep.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/BGP_LS_PCEP:Main</url>
+ <tag>HEAD</tag>
+ </scm>
+ <parent>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>bgp-parent</artifactId>
+ <version>0.6.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>bgp-path-selection-mode</artifactId>
+ <description>BGP Path Selection Mode</description>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-parser-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-rib-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-rib-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-broker-impl</artifactId>
+ </dependency>
+ <!-- test scope dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ org.opendaylight.protocol.bgp.mode.*,
+ org.opendaylight.controller.config.yang.bgp.path.selection.mode.*,
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.bgp.path.selection.mode.*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.api;
+
+import com.google.common.primitives.UnsignedInteger;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+public interface BestPath {
+
+ /**
+ *
+ * @return the routerId (UnsignedInteger)
+ */
+ UnsignedInteger getRouterId();
+
+ /**
+ *
+ * @return the routerId (UnsignedInteger) converted to a PeerId
+ */
+ PeerId getPeerId();
+
+ /**
+ *
+ * @return the path attributes
+ */
+ ContainerNode getAttributes();
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.api;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+public interface BestPathState {
+ Long getLocalPref();
+
+ Long getMultiExitDisc();
+
+ BgpOrigin getOrigin();
+
+ Long getPeerAs();
+
+ int getAsPathLength();
+
+ ContainerNode getAttributes();
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.api;
+
+public interface PathSelectionMode extends AutoCloseable {
+ /**
+ *
+ */
+ /**
+ * Create a RouteEntry
+ * @param isComplex true if is complex
+ * @return ComplexRouteEntry if is complex otherwise a SimpleRouteEntry
+ */
+ RouteEntry createRouteEntry(boolean isComplex);
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.api;
+
+import com.google.common.primitives.UnsignedInteger;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
+import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
+import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
+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.rib.TablesKey;
+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.MapEntryNode;
+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.
+ */
+public interface RouteEntry {
+ /**
+ * Remove route
+ *
+ * @param routerId router ID in unsigned integer format from an Ipv4Address
+ * @return return true if it was the last route on entry
+ */
+ boolean removeRoute(UnsignedInteger routerId);
+
+ /**
+ * Create value
+ *
+ * @param routeId router ID pathArgument
+ * @param path BestPath
+ * @return MapEntryNode
+ */
+ MapEntryNode createValue(PathArgument routeId, BestPath path);
+
+ /**
+ * Indicates whether best has changed
+ *
+ * @param localAs The local autonomous system number
+ * @return return true if it has changed
+ */
+ boolean selectBest(long localAs);
+
+ /**
+ * @param routerId router ID in unsigned integer format from an Ipv4Address
+ * @param attrII route Attributes Identifier
+ * @param data route Data change
+ * @return returns the offset
+ */
+ int addRoute(UnsignedInteger routerId, NodeIdentifier attrII, NormalizedNode<?, ?> data);
+
+ /**
+ * Update LocRibOut and AdjRibsOut by removing stale best path and writing new best
+ *
+ * @param localTK local Table Key
+ * @param peerPT peer export policy
+ * @param locRibTarget YII local rib
+ * @param ribSupport rib support
+ * @param discPeers list of disconnected peers
+ * @param tx DOM transaction
+ * @param routeIdPA router ID pathArgument
+ */
+ void updateRoute(TablesKey localTK, ExportPolicyPeerTracker peerPT, YangInstanceIdentifier locRibTarget, RIBSupport ribSupport,
+ CacheDisconnectedPeers discPeers, DOMDataWriteTransaction tx, PathArgument routeIdPA);
+
+ /**
+ * Write Route on LocRibOut and AdjRibsOut
+ * @param peerId destination peerId
+ * @param routeId router ID path Argument
+ * @param rootPath YII root path
+ * @param peerGroup PeerExportGroup
+ * @param localTK local Table Key
+ * @param ribSupport rib support
+ * @param tx DOM transaction
+ */
+ void writeRoute(PeerId peerId, PathArgument routeId, YangInstanceIdentifier rootPath, PeerExportGroup peerGroup, TablesKey localTK,
+ RIBSupport ribSupport, DOMDataWriteTransaction tx);
+}
\ No newline at end of file
* 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;
+package org.opendaylight.protocol.bgp.mode.impl;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.protocol.bgp.mode.api.BestPathState;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.AsPath;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.LocalPref;
import org.slf4j.LoggerFactory;
@NotThreadSafe
-final class BestPathState {
+public final class BestPathStateImpl implements BestPathState {
private static final class NamespaceSpecificIds {
private final Collection<PathArgument> asPath;
private final Collection<PathArgument> locPref;
}
}
- private static final Logger LOG = LoggerFactory.getLogger(BestPathState.class);
+ private static final Logger LOG = LoggerFactory.getLogger(BestPathStateImpl.class);
private static final Cache<QNameModule, NamespaceSpecificIds> PATH_CACHE = CacheBuilder.newBuilder().weakKeys().weakValues().build();
private long peerAs = 0L;
private BgpOrigin origin;
private boolean resolved;
- BestPathState(final ContainerNode attributes) {
+ public BestPathStateImpl(final ContainerNode attributes) {
final NamespaceSpecificIds col;
try {
col = PATH_CACHE.get(attributes.getNodeType().getModule(), new Callable<NamespaceSpecificIds>() {
if (maybeSegments.isPresent()) {
final UnkeyedListNode segments = (UnkeyedListNode) maybeSegments.get();
final List<Segments> segs = extractSegments(segments);
- if (segs.size() != 0) {
+ if (!segs.isEmpty()) {
this.peerAs = getPeerAs(segs).getValue();
this.asPathLength = countAsPath(segs);
}
this.resolved = true;
}
- Long getLocalPref() {
+ @Override
+ public Long getLocalPref() {
resolveValues();
return this.localPref;
}
- Long getMultiExitDisc() {
+ @Override
+ public Long getMultiExitDisc() {
resolveValues();
return this.multiExitDisc;
}
- BgpOrigin getOrigin() {
+ @Override
+ public BgpOrigin getOrigin() {
resolveValues();
return this.origin;
}
- Long getPeerAs() {
+ @Override
+ public Long getPeerAs() {
resolveValues();
return this.peerAs;
}
- int getAsPathLength() {
+ @Override
+ public int getAsPathLength() {
resolveValues();
return this.asPathLength;
}
return new AsNumber(0L);
}
- @VisibleForTesting
public List<Segments> extractSegments(final UnkeyedListNode segments) {
// list segments
final List<Segments> extracted = new ArrayList<>();
return null;
}
- ContainerNode getAttributes() {
+ @Override
+ public ContainerNode getAttributes() {
return this.attributes;
}
if (this == obj) {
return true;
}
- if (!(obj instanceof BestPathState)) {
+ if (!(obj instanceof BestPathStateImpl)) {
return false;
}
- final BestPathState other = (BestPathState) obj;
+ final BestPathStateImpl other = (BestPathStateImpl) obj;
if (!this.attributes.equals(other.attributes)) {
return false;
}
--- /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.mode.impl.base;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.primitives.UnsignedInteger;
+import java.util.Map;
+import java.util.Optional;
+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.mode.api.BestPath;
+import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
+import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
+import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
+import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
+import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
+import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibOut;
+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.Routes;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+abstract class BaseAbstractRouteEntry implements RouteEntry {
+
+ private static final NodeIdentifier ROUTES_IDENTIFIER = new NodeIdentifier(Routes.QNAME);
+ private static final Logger LOG = LoggerFactory.getLogger(BaseAbstractRouteEntry.class);
+ private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
+ private OffsetMap offsets = OffsetMap.EMPTY;
+ private ContainerNode[] values = EMPTY_ATTRIBUTES;
+ private Optional<BaseBestPath> bestPath = Optional.empty();
+ private Optional<BaseBestPath> removedBestPath = Optional.empty();
+
+ private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
+ int offset = this.offsets.offsetOf(routerId);
+ if (offset < 0) {
+ final OffsetMap newOffsets = this.offsets.with(routerId);
+ offset = newOffsets.offsetOf(routerId);
+
+ this.values = newOffsets.expand(this.offsets, this.values, offset);
+ this.offsets = newOffsets;
+ }
+
+ this.offsets.setValue(this.values, offset, attributes);
+ LOG.trace("Added route from {} attributes {}", routerId, attributes);
+ return offset;
+ }
+
+ /**
+ * Remove route
+ *
+ * @param routerId router ID in unsigned integer
+ * @param offset offset Offset of removed route
+ * @return true if its the last route
+ */
+ final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
+ this.values = this.offsets.removeValue(this.values, offset);
+ this.offsets = this.offsets.without(routerId);
+ return this.offsets.isEmty();
+ }
+
+ @Override
+ public final boolean selectBest(final long localAs) {
+ /*
+ * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
+ */
+ final BasePathSelector selector = new BasePathSelector(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);
+ LOG.trace("Processing router id {} attributes {}", routerId, attributes);
+ selector.processPath(routerId, attributes);
+ }
+
+ // Get the newly-selected best path.
+ final Optional<BaseBestPath> newBestPath = Optional.ofNullable(selector.result());
+ final boolean modified = !newBestPath.equals(this.bestPath);
+ if (modified) {
+ this.removedBestPath = this.bestPath;
+ }
+
+ LOG.trace("Previous best {}, current best {}, result {}", this.bestPath, newBestPath, modified);
+ this.bestPath = newBestPath;
+ return modified;
+ }
+
+ @Override
+ public int addRoute(final UnsignedInteger routerId, final NodeIdentifier attrII, final NormalizedNode<?, ?> data) {
+ LOG.trace("Find {} in {}", attrII, data);
+ final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attrII).orNull();
+ return addRoute(routerId, advertisedAttrs);
+ }
+
+ @Override
+ public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
+ final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
+ final YangInstanceIdentifier writePath = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
+ if (this.removedBestPath.isPresent()) {
+ removePathFromDataStore(this.removedBestPath.get(), routeIdPA, writePath, peerPT, localTK, ribSup, discPeers, tx);
+ this.removedBestPath = Optional.empty();
+ }
+ if (this.bestPath.isPresent()) {
+ addPathToDataStore(this.bestPath.get(), routeIdPA, writePath, ribSup, peerPT, localTK, discPeers, tx);
+ }
+ }
+
+ @Override
+ public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath,
+ final PeerExportGroup peerGroup, final TablesKey localTK, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
+ final BaseBestPath path = this.bestPath.get();
+ final ContainerNode effAttrib = peerGroup.effectiveAttributes(path.getPeerId(), path.getAttributes());
+ writeRoute(destPeer, getRouteTarget(ribSup, rootPath, routeId, localTK), effAttrib, createValue(routeId, path), ribSup, tx);
+ }
+
+ private void removePathFromDataStore(final BestPath path, final PathArgument routeIdPathArgument, final YangInstanceIdentifier writePath,
+ final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
+ final DOMDataWriteTransaction tx) {
+ LOG.trace("Best Path removed {}", path);
+ fillLocRib(writePath, tx, null);
+ fillAdjRibsOut(tx, null, null, routeIdPathArgument, path.getPeerId(), peerPT, localTK, ribSup, discPeers);
+ }
+
+ private void addPathToDataStore(final BestPath path, final PathArgument routeIdPathArgument, final YangInstanceIdentifier writePath,
+ final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final CacheDisconnectedPeers discPeers,
+ final DOMDataWriteTransaction tx) {
+ final NormalizedNode<?, ?> value = createValue(routeIdPathArgument, path);
+ LOG.trace("Selected best value {}", value);
+ fillLocRib(writePath, tx, value);
+ fillAdjRibsOut(tx, path.getAttributes(), value, routeIdPathArgument, path.getPeerId(), peerPT, localTK, ribSup, discPeers);
+ }
+
+ private boolean writeRoute(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
+ final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
+ if (effAttrib != null && value != null) {
+ LOG.debug("Write route {} to peer AdjRibsOut {}", value, destPeer);
+ tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
+ tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSup.routeAttributesIdentifier()), effAttrib);
+ return true;
+ }
+ return false;
+ }
+
+ final OffsetMap getOffsets() {
+ return this.offsets;
+ }
+
+ private void fillLocRib(final YangInstanceIdentifier writePath, final DOMDataWriteTransaction tx, final NormalizedNode<?, ?> value) {
+ if (value != null) {
+ LOG.debug("Write route to LocRib {}", value);
+ tx.put(LogicalDatastoreType.OPERATIONAL, writePath, value);
+ } else {
+ LOG.debug("Delete route from LocRib {}", writePath);
+ tx.delete(LogicalDatastoreType.OPERATIONAL, writePath);
+ }
+ }
+
+ @VisibleForTesting
+ private void fillAdjRibsOut(final DOMDataWriteTransaction tx, final ContainerNode attributes, final NormalizedNode<?, ?> value,
+ final PathArgument routeId, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup,
+ final CacheDisconnectedPeers discPeers) {
+ /*
+ * 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 (final PeerRole role : PeerRole.values()) {
+ if (PeerRole.Internal.equals(role)) {
+ continue;
+ }
+ final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
+ if (peerGroup != null) {
+ final ContainerNode effAttrib = peerGroup.effectiveAttributes(routePeerId, attributes);
+ peerGroup.getPeers().stream()
+ .filter(pid -> filterRoutes(pid, routePeerId, peerPT, localTK, discPeers))
+ .forEach(pid -> update(pid.getKey(), getRouteTarget(ribSup, pid.getValue(), routeId, localTK), effAttrib, value,
+ ribSup, tx));
+ }
+ }
+ }
+
+ private boolean filterRoutes(final Map.Entry<PeerId, YangInstanceIdentifier> pid, final PeerId destPeer, final ExportPolicyPeerTracker peerPT,
+ final TablesKey localTK, final CacheDisconnectedPeers discPeers) {
+ final PeerId peerId = pid.getKey();
+ return !peerId.equals(destPeer) && isTableSupported(destPeer, peerPT, localTK) && !discPeers.isPeerDisconnected(destPeer);
+ }
+
+ private void update(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
+ final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
+ if (!writeRoute(destPeer, routeTarget, effAttrib, value, ribSup, tx)) {
+ LOG.trace("Removing {} from transaction for peer {}", routeTarget, destPeer);
+ tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
+ }
+ }
+
+ private boolean isTableSupported(final PeerId destPeer, final ExportPolicyPeerTracker peerPT, final TablesKey localTK) {
+ if (!peerPT.isTableSupported(destPeer, localTK)) {
+ LOG.trace("Route rejected, peer {} does not support this table type {}", destPeer, localTK);
+ return false;
+ }
+ return true;
+ }
+
+
+ private YangInstanceIdentifier getRouteTarget(final RIBSupport ribSup, final YangInstanceIdentifier rootPath, final PathArgument routeId,
+ final TablesKey localTK) {
+ return ribSup.routePath(rootPath.node(AdjRibOut.QNAME).node(Tables.QNAME).node(RibSupportUtils.toYangTablesKey(localTK)).
+ node(ROUTES_IDENTIFIER), routeId);
+ }
+}
\ No newline at end of file
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import javax.annotation.Nonnull;
+import org.opendaylight.protocol.bgp.mode.api.BestPathState;
+import org.opendaylight.protocol.bgp.mode.spi.AbstractBestPath;
+import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
-final class BestPath {
+final class BaseBestPath extends AbstractBestPath {
private final UnsignedInteger routerId;
- private final BestPathState state;
-
- BestPath(@Nonnull final UnsignedInteger routerId, @Nonnull final BestPathState state) {
+ BaseBestPath(@Nonnull final UnsignedInteger routerId, @Nonnull final BestPathState state) {
+ super(state);
this.routerId = Preconditions.checkNotNull(routerId);
- this.state = Preconditions.checkNotNull(state);
}
- UnsignedInteger getRouterId() {
+ @Override
+ public UnsignedInteger getRouterId() {
return this.routerId;
}
- BestPathState getState() {
- return this.state;
+ @Override
+ public PeerId getPeerId() {
+ return RouterIds.createPeerId(this.routerId);
}
private ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
if (this == obj) {
return true;
}
- if (!(obj instanceof BestPath)) {
+ if (!(obj instanceof BaseBestPath)) {
return false;
}
- final BestPath other = (BestPath) obj;
+ final BaseBestPath other = (BaseBestPath) obj;
if (!this.routerId.equals(other.routerId)) {
return false;
}
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import com.google.common.primitives.UnsignedInteger;
+import org.opendaylight.protocol.bgp.mode.api.BestPath;
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.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-final class ComplexRouteEntry extends AbstractRouteEntry {
+final class BaseComplexRouteEntry extends BaseAbstractRouteEntry {
private static final MapEntryNode[] EMPTY_VALUES = new MapEntryNode[0];
private MapEntryNode[] values = EMPTY_VALUES;
@Override
- protected int addRoute(final UnsignedInteger routerId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
+ public int addRoute(final UnsignedInteger routerId, final NodeIdentifier attrII, final NormalizedNode<?, ?> data) {
final OffsetMap oldMap = getOffsets();
- final int offset = super.addRoute(routerId, attributesIdentifier, data);
+ final int offset = super.addRoute(routerId, attrII, data);
final OffsetMap newMap = getOffsets();
if (!newMap.equals(oldMap)) {
}
@Override
- protected MapEntryNode createValue(final PathArgument routeId) {
+ public MapEntryNode createValue(final PathArgument routeId, final BestPath path) {
final OffsetMap map = getOffsets();
- final int offset = map.offsetOf(getBestRouterId());
+ final int offset = map.offsetOf(path.getRouterId());
return map.getValue(this.values, offset);
}
@Override
- boolean removeRoute(final UnsignedInteger routerId) {
+ public boolean removeRoute(final UnsignedInteger routerId) {
final OffsetMap map = getOffsets();
final int offset = map.offsetOf(routerId);
final boolean ret = removeRoute(routerId, offset);
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.impl.base;
+
+import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
+import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
+
+final class BasePathSelection implements PathSelectionMode {
+ @Override
+ public RouteEntry createRouteEntry(final boolean isComplex) {
+ return isComplex ? new BaseComplexRouteEntry() : new BaseSimpleRouteEntry();
+ }
+
+ @Override
+ public void close() throws Exception {
+ //no-op
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.impl.base;
+
+import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
+
+public final class BasePathSelectionModeFactory {
+
+ private BasePathSelectionModeFactory() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static PathSelectionMode createBestPathSelectionStrategy() {
+ return new BasePathSelection();
+ }
+}
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import java.util.Collection;
import javax.annotation.Nonnull;
+import org.opendaylight.protocol.bgp.mode.api.BestPathState;
+import org.opendaylight.protocol.bgp.mode.impl.BestPathStateImpl;
import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
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.types.rev130919.BgpOrigin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-final class BestPathSelector {
- private static final Logger LOG = LoggerFactory.getLogger(BestPathSelector.class);
+final class BasePathSelector {
+ private static final Logger LOG = LoggerFactory.getLogger(BasePathSelector.class);
private static final Collection<PathArgument> ORIGINATOR_ID = ImmutableList.<PathArgument>of(new NodeIdentifier(OriginatorId.QNAME), new NodeIdentifier(QName.create(OriginatorId.QNAME, "originator")));
private final Long ourAs;
private UnsignedInteger bestRouterId = null;
private BestPathState bestState = null;
- BestPathSelector(final Long ourAs) {
+ BasePathSelector(final Long ourAs) {
this.ourAs = Preconditions.checkNotNull(ourAs);
}
* Store the new details if we have nothing stored or when the selection algorithm indicates new details
* are better.
*/
- final BestPathState state = new BestPathState(attrs);
+ final BestPathState state = new BestPathStateImpl(attrs);
if (this.bestOriginatorId == null || !isExistingPathBetter(state)) {
LOG.trace("Selecting path from router {}", routerId);
this.bestOriginatorId = originatorId;
}
}
- BestPath result() {
- return this.bestRouterId == null ? null : new BestPath(this.bestRouterId, this.bestState);
+ BaseBestPath result() {
+ return this.bestRouterId == null ? null : new BaseBestPath(this.bestRouterId, this.bestState);
}
/**
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import com.google.common.primitives.UnsignedInteger;
+import org.opendaylight.protocol.bgp.mode.api.BestPath;
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.MapEntryNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-final class SimpleRouteEntry extends AbstractRouteEntry {
+final class BaseSimpleRouteEntry extends BaseAbstractRouteEntry {
@Override
- boolean removeRoute(UnsignedInteger routerId) {
+ public boolean removeRoute(UnsignedInteger routerId) {
return removeRoute(routerId, getOffsets().offsetOf(routerId));
}
@Override
- protected MapEntryNode createValue(final PathArgument routeId) {
+ public MapEntryNode createValue(final PathArgument routeId, final BestPath path) {
final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> b = Builders.mapEntryBuilder();
b.withNodeIdentifier((NodeIdentifierWithPredicates) routeId);
- b.addChild(attributes());
+ b.addChild(path.getAttributes());
return b.build();
}
}
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
/**
* A map of Router identifier to an offset. Used to maintain a simple
- * offset-based lookup across multiple {@link AbstractRouteEntry} objects,
+ * offset-based lookup across multiple {@link BaseAbstractRouteEntry} objects,
* which share either contributors or consumers.
- * <p/>
* We also provide utility reformat methods, which provide access to
* array members and array management features.
*/
final class OffsetMap {
- private static final Logger LOG = LoggerFactory.getLogger(OffsetMap.class);
static final OffsetMap EMPTY = new OffsetMap(Collections.<UnsignedInteger>emptySet());
+ private static final Logger LOG = LoggerFactory.getLogger(OffsetMap.class);
private static final LoadingCache<Set<UnsignedInteger>, OffsetMap> OFFSETMAPS = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<Set<UnsignedInteger>, OffsetMap>() {
@Override
public OffsetMap load(final Set<UnsignedInteger> key) throws Exception {
private final UnsignedInteger[] routerIds;
private OffsetMap(final Set<UnsignedInteger> routerIds) {
- final UnsignedInteger[] array = routerIds.toArray(new UnsignedInteger[0]);
+ final UnsignedInteger[] array = routerIds.toArray(new UnsignedInteger[routerIds.size()]);
Arrays.sort(array, IPV4_COMPARATOR);
this.routerIds = array;
}
return this.routerIds.length;
}
+ boolean isEmty() {
+ return this.routerIds.length == 0;
+ }
+
OffsetMap with(final UnsignedInteger routerId) {
// TODO: we could make this faster if we had an array-backed Set and requiring
// the caller to give us the result of offsetOf() -- as that indicates
--- /dev/null
+/*
+ * Copyright (c) 2016 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.mode.spi;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import org.opendaylight.protocol.bgp.mode.api.BestPath;
+import org.opendaylight.protocol.bgp.mode.api.BestPathState;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+public abstract class AbstractBestPath implements BestPath {
+ protected final BestPathState state;
+
+ protected AbstractBestPath(final BestPathState state) {
+ this.state = Preconditions.checkNotNull(state);
+ }
+
+ @VisibleForTesting
+ public final BestPathState getState() {
+ return this.state;
+ }
+
+ @Override
+ public final ContainerNode getAttributes() {
+ return this.state.getAttributes();
+ }
+}
\ No newline at end of file
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module odl-bgp-path-selection-mode {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:bgp:path:selection:mode";
+ prefix "psm";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ organization "Cisco Systems, Inc.";
+
+ contact "Claudio D. Gasparini <cgaspari@cisco.com>";
+
+ description
+ "This module contains the base YANG definitions for
+ BGP Best path selection mode.
+
+ Copyright (c)2016 Cisco Systems, Inc. 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";
+
+ revision "2016-03-01" {
+ description
+ "Initial revision";
+ }
+
+ identity path-selection-mode-factory {
+ description
+ "Service representing a path selection mode.";
+
+ base "config:service-type";
+ config:java-class "org.opendaylight.protocol.bgp.mode.api.PathSelectionMode";
+ }
+}
\ No newline at end of file
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
+import org.opendaylight.protocol.bgp.mode.impl.BestPathStateImpl;
import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.Segments;
public class BestPathSelectorTest {
- static final QName ATTRS_EXTENSION_Q = QName.create("urn:opendaylight:params:xml:ns:yang:bgp-inet", "2015-03-05", "attributes");
+ public static final QName ATTRS_EXTENSION_Q = QName.create("urn:opendaylight:params:xml:ns:yang:bgp-inet", "2015-03-05", "attributes");
private final QName localPrefQName = QName.create(ATTRS_EXTENSION_Q, "local-pref");
private final QName multiExitDiscQName = QName.create(ATTRS_EXTENSION_Q, "multi-exit-disc");
private final QName originQName = QName.create(ATTRS_EXTENSION_Q, "origin");
private final UnsignedInteger ROUTER_ID = RouterIds.routerIdForAddress("127.0.0.1");
private final UnsignedInteger ROUTER_ID2 = RouterIds.routerIdForPeerId(new PeerId("bgp://127.0.0.1"));
private final UnsignedInteger ROUTER_ID3 = RouterIds.routerIdForPeerId(new PeerId("bgp://127.0.0.2"));
- private final BestPathState state = new BestPathState(createStateFromPrefMedOriginASPath());
- private final BestPath originBestPath = new BestPath(this.ROUTER_ID, this.state);
- private final BestPathSelector selector = new BestPathSelector(20L);
+ private final BestPathStateImpl state = new BestPathStateImpl(createStateFromPrefMedOriginASPath());
+ private final BaseBestPath originBestPath = new BaseBestPath(this.ROUTER_ID, this.state);
+ private final BasePathSelector selector = new BasePathSelector(20L);
private DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> dataContBuilder;
- static final QName AS_NUMBER_Q = QName.create(ATTRS_EXTENSION_Q, "as-number");
- static final NodeIdentifier SEGMENTS_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, Segments.QNAME.getLocalName()));
- static final NodeIdentifier SET_LEAFLIST_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-set"));
- static final NodeIdentifier SEQ_LEAFLIST_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-sequence"));
+ public static final QName AS_NUMBER_Q = QName.create(ATTRS_EXTENSION_Q, "as-number");
+ public static final NodeIdentifier SEGMENTS_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, Segments.QNAME.getLocalName()));
+ private static final NodeIdentifier SET_LEAFLIST_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-set"));
+ public static final NodeIdentifier SEQ_LEAFLIST_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-sequence"));
- static final UnkeyedListEntryNode SET_SEGMENT = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
+ public static final UnkeyedListEntryNode SET_SEGMENT = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
.addChild(Builders.leafSetBuilder().withNodeIdentifier(SET_LEAFLIST_NID)
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 10L)).withValue(10L).build())
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 11L)).withValue(11L).build())
.build()).build();
- static final UnkeyedListEntryNode SEQ_SEGMENT = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
+ public static final UnkeyedListEntryNode SEQ_SEGMENT = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
.addChild(Builders.orderedLeafSetBuilder().withNodeIdentifier(SEQ_LEAFLIST_NID)
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 1L)).withValue(1L).build())
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 2L)).withValue(2L).build())
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 3L)).withValue(3L).build())
.build()).build();
- static final UnkeyedListEntryNode SEQ_SEGMENT2 = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
+ private static final UnkeyedListEntryNode SEQ_SEGMENT2 = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
.addChild(Builders.orderedLeafSetBuilder().withNodeIdentifier(SEQ_LEAFLIST_NID)
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 20L)).withValue(20L).build())
.addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 2L)).withValue(2L).build())
@Test
public void testBestPathForEquality() {
this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOriginASPath());
- final BestPath processedPath = this.selector.result();
+ final BaseBestPath processedPath = this.selector.result();
assertEquals(this.originBestPath.getRouterId(), processedPath.getRouterId());
assertEquals(this.originBestPath.getState().getLocalPref(), processedPath.getState().getLocalPref());
@Test
public void testBestPathWithHigherLocalPref() {
this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOrigin()); // local-pref 123
- BestPath processedPath = this.selector.result();
+ BaseBestPath processedPath = this.selector.result();
assertEquals(123L, processedPath.getState().getLocalPref().longValue());
this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOriginASPath()); // local-pref 321
@Test
public void testBestPathSelectionOptions() {
this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOriginASPath());
- BestPath processedPath = this.selector.result();
+ BaseBestPath processedPath = this.selector.result();
assertEquals(1, processedPath.getState().getOrigin().getIntValue());
addIgpOrigin(); // prefer the path with the lowest origin type
@Test
public void testBestPathForNonEquality() {
this.selector.processPath(this.ROUTER_ID3, createStateFromPrefMedOrigin());
- final BestPath processedPath = this.selector.result();
+ final BaseBestPath processedPath = this.selector.result();
- assertNotEquals(this.originBestPath.getRouterId(), processedPath.getRouterId());
+ assertNotEquals(this.originBestPath.getPeerId(), processedPath.getPeerId());
assertNotEquals(this.originBestPath.getState().getLocalPref(), processedPath.getState().getLocalPref());
assertNotEquals(this.originBestPath.getState().getMultiExitDisc(), processedPath.getState().getMultiExitDisc());
assertNotEquals(this.originBestPath.getState().getOrigin(), processedPath.getState().getOrigin());
// test
final List<Segments> actual = this.state.extractSegments(builder.build());
assertEquals(expected.size(), actual.size());
- assertEquals(Sets.newHashSet(1,2,3), Sets.newHashSet(1,3,2));
+ assertEquals(Sets.newHashSet(1, 2, 3), Sets.newHashSet(1, 3, 2));
assertEquals(Sets.newHashSet(expected.get(0).getAsSet()), Sets.newHashSet(actual.get(0).getAsSet()));
assertEquals(expected.get(1), actual.get(1));
}
public void testBgpOrigin() {
final ContainerNode containerIncom = this.dataContBuilder.addChild(createContBuilder(this.originQName).addChild(createValueBuilder("incomplete", this.originQName, "value").build()).build()).build();
this.selector.processPath(this.ROUTER_ID3, containerIncom);
- final BestPath processedPathIncom = this.selector.result();
+ final BaseBestPath processedPathIncom = this.selector.result();
assertEquals(BgpOrigin.Incomplete, processedPathIncom.getState().getOrigin());
final ContainerNode containerException = this.dataContBuilder.addChild(createContBuilder(this.originQName).addChild(createValueBuilder("LOL", this.originQName, "value").build()).build()).build();
this.selector.processPath(this.ROUTER_ID3, containerException);
- final BestPath processedPathException = this.selector.result();
+ final BaseBestPath processedPathException = this.selector.result();
processedPathException.getState().getOrigin();
}
}
* 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;
+package org.opendaylight.protocol.bgp.mode.impl.base;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
private final String LOCAL_ADDRESS = "127.0.0.1";
private final int LOCAL_ADDRESS_DECIMAL = 0x7f000001;
- private final Integer[] TESTED_VALUES = { 1, 2, 3, 4, 5, 6, 7 };
- private final Integer[] TESTED_VALUES_REMOVE = {2, 3, 4, 5, 6, 7 };
+ private final Integer[] TESTED_VALUES = {1, 2, 3, 4, 5, 6, 7};
+ private final Integer[] TESTED_VALUES_REMOVE = {2, 3, 4, 5, 6, 7};
private final int EXPECTED_ROUTER_OFFSET = 0;
private final int EXPECTED_VALUE = 1;
private final int CHANGED_VALUE = 111;
<module>openconfig-api</module>
<module>openconfig-spi</module>
<module>openconfig-impl</module>
+ <module>path-selection-mode</module>
</modules>
</project>
<groupId>${project.groupId}</groupId>
<artifactId>bgp-openconfig-spi</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-path-selection-mode</artifactId>
+ </dependency>
<dependency>
<groupId>org.osgi</groupId>
</dependency>
<!-- Testing dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-path-selection-mode</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataContainer;
-public class AddPathImplModule extends AbstractAddPathImplModule {
+public final class AddPathImplModule extends AbstractAddPathImplModule {
public AddPathImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
--- /dev/null
+/*
+ * Copyright (c) 2016 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.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPBestPathSelection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+
+public final class BGPPSMImplModule extends AbstractBGPPSMImplModule {
+ public BGPPSMImplModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public BGPPSMImplModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver, BGPPSMImplModule oldModule, AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ private static final class AutoCloseableBestPathSelectionStrategy implements BGPBestPathSelection {
+
+ private final BgpTableType pathFamilyDependency;
+ private final PathSelectionMode strategyFactory;
+
+ public AutoCloseableBestPathSelectionStrategy(final BgpTableType pathFamilyDependency, final PathSelectionMode strategyFactory) {
+ this.pathFamilyDependency = Preconditions.checkNotNull(pathFamilyDependency);
+ this.strategyFactory = Preconditions.checkNotNull(strategyFactory);
+ }
+
+ @Override
+ public void close() throws Exception {
+ //no op
+ }
+
+ @Override
+ public Class<? extends AddressFamily> getAfi() {
+ return this.pathFamilyDependency.getAfi();
+ }
+
+ @Override
+ public Class<? extends SubsequentAddressFamily> getSafi() {
+ return this.pathFamilyDependency.getSafi();
+ }
+
+ @Override
+ public Class<? extends DataContainer> getImplementedInterface() {
+ return BGPBestPathSelection.class;
+ }
+
+ @Override
+ public PathSelectionMode getStrategy() {
+ return this.strategyFactory;
+ }
+ }
+
+ @Override
+ public void customValidation() {
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new AutoCloseableBestPathSelectionStrategy(getPathAddressFamilyDependency(), getPathSelectionModeDependency());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 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.impl;
+public class BGPPSMImplModuleFactory extends AbstractBGPPSMImplModuleFactory {
+
+}
*/
package org.opendaylight.controller.config.yang.bgp.rib.impl;
+import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenconfigMapper;
import org.opendaylight.protocol.bgp.openconfig.spi.InstanceConfigurationIdentifier;
import org.opendaylight.protocol.bgp.openconfig.spi.pojo.BGPRibInstanceConfiguration;
import org.opendaylight.protocol.bgp.rib.impl.RIBImpl;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPBestPathSelection;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.osgi.framework.BundleContext;
@Override
public java.lang.AutoCloseable createInstance() {
final AsNumber asNumber = new AsNumber(getLocalAs());
+ final Map<TablesKey, PathSelectionMode> pathSelectionStrategies = mapBestPathSelectionStrategyByFamily(getPathSelectionModeDependency());
final RIBImpl rib = new RIBImpl(getRibId(), asNumber, getBgpRibId(), getClusterId(), getExtensionsDependency(),
getBgpDispatcherDependency(), getTcpReconnectStrategyDependency(), getCodecTreeFactoryDependency(), getSessionReconnectStrategyDependency(),
- getDataProviderDependency(), getDomDataProviderDependency(), getLocalTableDependency(), classLoadingStrategy(),
+ getDataProviderDependency(), getDomDataProviderDependency(), getLocalTableDependency(), pathSelectionStrategies, classLoadingStrategy(),
new RIBImplModuleTracker(getGlobalWriter()), getOpenconfigProviderDependency());
registerSchemaContextListener(rib);
return rib;
}
}
+ private Map<TablesKey, PathSelectionMode> mapBestPathSelectionStrategyByFamily(final List<BGPBestPathSelection> bestPathSelectionDependency) {
+ return Collections.unmodifiableMap(bestPathSelectionDependency.stream().collect(
+ Collectors.toMap(st -> new TablesKey(st.getAfi(), st.getSafi()), st -> st.getStrategy())));
+ }
+
public void setBundleContext(final BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
+++ /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 javax.annotation.concurrent.NotThreadSafe;
-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.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * 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
-abstract class AbstractRouteEntry {
-
- private static final Logger LOG = LoggerFactory.getLogger(AbstractRouteEntry.class);
-
- private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
-
- private OffsetMap offsets = OffsetMap.EMPTY;
- private ContainerNode[] values = EMPTY_ATTRIBUTES;
- private BestPath bestPath;
-
- private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
- int offset = this.offsets.offsetOf(routerId);
- if (offset < 0) {
- final OffsetMap newOffsets = this.offsets.with(routerId);
- offset = newOffsets.offsetOf(routerId);
-
- final ContainerNode[] newAttributes = newOffsets.expand(this.offsets, this.values, offset);
- this.values = newAttributes;
- this.offsets = newOffsets;
- }
-
- this.offsets.setValue(this.values, offset, attributes);
- LOG.trace("Added route from {} attributes {}", routerId, attributes);
- return offset;
- }
-
- protected int addRoute(final UnsignedInteger routerId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
- LOG.trace("Find {} in {}", attributesIdentifier, data);
- final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
- return addRoute(routerId, advertisedAttrs);
- }
-
- /**
- * Remove route
- * @param routerId
- * @param offset
- * @return true if it was the last route
- */
- protected final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
- if (this.offsets.size() != 1) {
- this.values = this.offsets.removeValue(this.values, offset);
- this.offsets = this.offsets.without(routerId);
- return false;
- } else {
- return true;
- }
- }
-
- // Indicates whether best has changed
- final 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);
- LOG.trace("Processing router id {} attributes {}", routerId, attributes);
- selector.processPath(routerId, attributes);
- }
-
- // Get the newly-selected best path.
- final BestPath newBestPath = selector.result();
- final boolean ret = !newBestPath.equals(this.bestPath);
- LOG.trace("Previous best {}, current best {}, result {}", this.bestPath, newBestPath, ret);
- this.bestPath = newBestPath;
- return ret;
- }
-
- final ContainerNode attributes() {
- return this.bestPath.getState().getAttributes();
- }
-
- protected final OffsetMap getOffsets() {
- return this.offsets;
- }
-
- protected final UnsignedInteger getBestRouterId() {
- return this.bestPath.getRouterId();
- }
-
- abstract boolean removeRoute(final UnsignedInteger routerId);
- abstract MapEntryNode createValue(PathArgument routeId);
-}
*/
package org.opendaylight.protocol.bgp.rib.impl;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import java.util.Map.Entry;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
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.mode.api.PathSelectionMode;
+import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
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.LocRib;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibOut;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.EffectiveRibIn;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.SupportedTables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
private static final Logger LOG = LoggerFactory.getLogger(LocRibWriter.class);
private static final LeafNode<Boolean> ATTRIBUTES_UPTODATE_TRUE = ImmutableNodes.leafNode(QName.create(Attributes.QNAME, "uptodate"), Boolean.TRUE);
- private static final NodeIdentifier ROUTES_IDENTIFIER = new NodeIdentifier(Routes.QNAME);
private static final NodeIdentifier EFFRIBIN_NID = new NodeIdentifier(EffectiveRibIn.QNAME);
private static final NodeIdentifier TABLES_NID = new NodeIdentifier(Tables.QNAME);
- private final Map<PathArgument, AbstractRouteEntry> routeEntries = new HashMap<>();
+ private final Map<PathArgument, RouteEntry> routeEntries = new HashMap<>();
private final YangInstanceIdentifier locRibTarget;
private final DOMTransactionChain chain;
private final ExportPolicyPeerTracker peerPolicyTracker;
private final TablesKey localTablesKey;
private final ListenerRegistration<LocRibWriter> reg;
private final CacheDisconnectedPeers cacheDisconnectedPeers;
+ private final PathSelectionMode pathSelectionMode;
private LocRibWriter(final RIBSupportContextRegistry registry, final DOMTransactionChain chain, final YangInstanceIdentifier target, final Long ourAs,
- final DOMDataTreeChangeService service, final PolicyDatabase pd, final TablesKey tablesKey, final CacheDisconnectedPeers cacheDisconnectedPeers) {
+ final DOMDataTreeChangeService service, final PolicyDatabase pd, final TablesKey tablesKey, final CacheDisconnectedPeers cacheDisconnectedPeers,
+ @Nonnull final PathSelectionMode pathSelectionMode) {
this.chain = Preconditions.checkNotNull(chain);
this.tableKey = RibSupportUtils.toYangTablesKey(tablesKey);
this.localTablesKey = tablesKey;
this.attributesIdentifier = this.ribSupport.routeAttributesIdentifier();
this.peerPolicyTracker = new ExportPolicyPeerTrackerImpl(pd);
this.cacheDisconnectedPeers = cacheDisconnectedPeers;
+ this.pathSelectionMode = pathSelectionMode;
final DOMDataWriteTransaction tx = this.chain.newWriteOnlyTransaction();
tx.merge(LogicalDatastoreType.OPERATIONAL, this.locRibTarget.node(Routes.QNAME), this.ribSupport.emptyRoutes());
this.reg = service.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, tableId), this);
}
- public static LocRibWriter create(@Nonnull final RIBSupportContextRegistry registry, @Nonnull final TablesKey tablesKey, @Nonnull final DOMTransactionChain chain, @Nonnull final YangInstanceIdentifier target,
- @Nonnull final AsNumber ourAs, @Nonnull final DOMDataTreeChangeService service, @Nonnull final PolicyDatabase pd, final CacheDisconnectedPeers cacheDisconnectedPeers) {
- return new LocRibWriter(registry, chain, target, ourAs.getValue(), service, pd, tablesKey, cacheDisconnectedPeers);
+ public static LocRibWriter create(@Nonnull final RIBSupportContextRegistry registry, @Nonnull final TablesKey tablesKey, @Nonnull final DOMTransactionChain chain,
+ @Nonnull final YangInstanceIdentifier target, @Nonnull final AsNumber ourAs, @Nonnull final DOMDataTreeChangeService service, @Nonnull final PolicyDatabase pd,
+ final CacheDisconnectedPeers cacheDisconnectedPeers, @Nonnull final PathSelectionMode pathSelectionStrategy) {
+ return new LocRibWriter(registry, chain, target, ourAs.getValue(), service, pd, tablesKey, cacheDisconnectedPeers, pathSelectionStrategy);
}
@Override
}
@Nonnull
- private AbstractRouteEntry createEntry(final PathArgument routeId) {
- final AbstractRouteEntry ret = this.ribSupport.isComplexRoute() ? new ComplexRouteEntry() : new SimpleRouteEntry();
+ private RouteEntry createEntry(final PathArgument routeId) {
+ final RouteEntry ret = this.pathSelectionMode.createRouteEntry(ribSupport.isComplexRoute());
this.routeEntries.put(routeId, ret);
LOG.trace("Created new entry for {}", routeId);
return ret;
* We use two-stage processing here in hopes that we avoid duplicate
* calculations when multiple peers have changed a particular entry.
*/
- final Map<RouteUpdateKey, AbstractRouteEntry> toUpdate = update(tx, changes);
+ final Map<RouteUpdateKey, RouteEntry> toUpdate = update(tx, changes);
// Now walk all updated entries
walkThrough(tx, toUpdate);
}
}
- private Map<RouteUpdateKey, AbstractRouteEntry> update(final DOMDataWriteTransaction tx, final Collection<DataTreeCandidate> changes) {
- final Map<RouteUpdateKey, AbstractRouteEntry> ret = new HashMap<>();
+ private Map<RouteUpdateKey, RouteEntry> update(final DOMDataWriteTransaction tx, final Collection<DataTreeCandidate> changes) {
+ final Map<RouteUpdateKey, RouteEntry> ret = new HashMap<>();
for (final DataTreeCandidate tc : changes) {
final YangInstanceIdentifier rootPath = tc.getRootPath();
}
private void filterOutAnyChangeOutsideEffRibsIn(final PeerId peerId, final DataTreeCandidateNode rootNode,
- final Map<RouteUpdateKey, AbstractRouteEntry> ret, final YangInstanceIdentifier rootPath, final DOMDataWriteTransaction tx) {
+ final Map<RouteUpdateKey, RouteEntry> ret, final YangInstanceIdentifier rootPath, final DOMDataWriteTransaction tx) {
final DataTreeCandidateNode ribIn = rootNode.getModifiedChild(EFFRIBIN_NID);
if (ribIn == null) {
LOG.trace("Skipping change {}", rootNode.getIdentifier());
if (tablesChange != null) {
final NodeIdentifierWithPredicates supTablesKey = RibSupportUtils.toYangKey(SupportedTables.QNAME, this.localTablesKey);
final DataTreeCandidateNode containsLocalKeyTable = tablesChange.getModifiedChild(supTablesKey);
- if(containsLocalKeyTable != null) {
+ if (containsLocalKeyTable != null) {
this.peerPolicyTracker.onTablesChanged(peerIdOfNewPeer, containsLocalKeyTable);
}
}
private void initializeTableWithExistenRoutes(final DataTreeCandidateNode table, final PeerId peerIdOfNewPeer, final YangInstanceIdentifier rootPath,
final DOMDataWriteTransaction tx) {
- if (!table.getDataBefore().isPresent() && isTableSupported(peerIdOfNewPeer)) {
+ if (!table.getDataBefore().isPresent() && this.peerPolicyTracker.isTableSupported(peerIdOfNewPeer, this.localTablesKey)) {
LOG.debug("Peer {} table has been created, inserting existent routes", peerIdOfNewPeer);
final PeerRole newPeerRole = this.peerPolicyTracker.getRole(IdentifierUtils.peerPath(rootPath));
final PeerExportGroup peerGroup = this.peerPolicyTracker.getPeerGroup(newPeerRole);
- for (Map.Entry<PathArgument, AbstractRouteEntry> entry : this.routeEntries.entrySet()) {
- final AbstractRouteEntry routeEntry = entry.getValue();
- final PathArgument routeId = entry.getKey();
- final YangInstanceIdentifier routeTarget = getRouteTarget(rootPath, routeId);
- final NormalizedNode<?, ?> value = routeEntry.createValue(routeId);
- final PeerId routePeerId = RouterIds.createPeerId(routeEntry.getBestRouterId());
- final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(routePeerId, routeEntry.attributes());
- if (effectiveAttributes != null && value != null) {
- LOG.debug("Write route {} to peer AdjRibsOut {}", value, peerIdOfNewPeer);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(this.attributesIdentifier), effectiveAttributes);
- }
- }
+ this.routeEntries.entrySet().forEach(entry -> entry.getValue().writeRoute(peerIdOfNewPeer, entry.getKey(), rootPath, peerGroup,
+ this.localTablesKey, this.ribSupport, tx));
}
}
- private YangInstanceIdentifier getRouteTarget(final YangInstanceIdentifier rootPath, final PathArgument routeId) {
- return this.ribSupport.routePath(rootPath.node(AdjRibOut.QNAME).node(Tables.QNAME).node(this.tableKey).node(ROUTES_IDENTIFIER), routeId);
- }
-
private void filterOutPeerRole(final PeerId peerId, final DataTreeCandidateNode rootNode, final YangInstanceIdentifier rootPath) {
final DataTreeCandidateNode roleChange = rootNode.getModifiedChild(AbstractPeerRoleTracker.PEER_ROLE_NID);
if (roleChange != null) {
}
private void updateNodes(final DataTreeCandidateNode table, final PeerId peerId, final DOMDataWriteTransaction tx,
- final Map<RouteUpdateKey, AbstractRouteEntry> routes) {
+ final Map<RouteUpdateKey, RouteEntry> routes) {
for (final DataTreeCandidateNode child : table.getChildNodes()) {
LOG.debug("Modification type {}", child.getModificationType());
if ((Attributes.QNAME).equals(child.getIdentifier().getNodeType())) {
}
}
- private void updateRoutesEntries(final DataTreeCandidateNode child, final PeerId peerId, final Map<RouteUpdateKey, AbstractRouteEntry> routes) {
+ private void updateRoutesEntries(final DataTreeCandidateNode child, final PeerId peerId, final Map<RouteUpdateKey, RouteEntry> routes) {
final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId);
final Collection<DataTreeCandidateNode> modifiedRoutes = this.ribSupport.changedRoutes(child);
for (final DataTreeCandidateNode route : modifiedRoutes) {
final PathArgument routeId = route.getIdentifier();
- AbstractRouteEntry entry = this.routeEntries.get(routeId);
-
+ RouteEntry entry = this.routeEntries.get(routeId);
final Optional<NormalizedNode<?, ?>> maybeData = route.getDataAfter();
- final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(peerId, routeId);
if (maybeData.isPresent()) {
if (entry == null) {
entry = createEntry(routeId);
} else if (entry != null && entry.removeRoute(routerId)) {
this.routeEntries.remove(routeId);
LOG.trace("Removed route from {}", routerId);
- entry = null;
}
+ final RouteUpdateKey routeUpdateKey = new RouteUpdateKey(peerId, routeId);
LOG.debug("Updated route {} entry {}", routeId, entry);
routes.put(routeUpdateKey, entry);
}
}
- private void walkThrough(final DOMDataWriteTransaction tx, final Map<RouteUpdateKey, AbstractRouteEntry> toUpdate) {
- for (final Entry<RouteUpdateKey, AbstractRouteEntry> e : toUpdate.entrySet()) {
+ private void walkThrough(final DOMDataWriteTransaction tx, final Map<RouteUpdateKey, RouteEntry> toUpdate) {
+ for (final Map.Entry<RouteUpdateKey, RouteEntry> e : toUpdate.entrySet()) {
LOG.trace("Walking through {}", e);
- final AbstractRouteEntry entry = e.getValue();
- final RouteUpdateKey key = e.getKey();
- final NormalizedNode<?, ?> value;
- final PathArgument routeId = key.getRouteId();
- PeerId routePeerId = key.getPeerId();
- if (entry != null) {
- if (!entry.selectBest(this.ourAs)) {
- // Best path has not changed, no need to do anything else. Proceed to next route.
- LOG.trace("Continuing");
- continue;
- }
- routePeerId = RouterIds.createPeerId(entry.getBestRouterId());
- value = entry.createValue(routeId);
- LOG.trace("Selected best value {}", value);
- } else {
- value = null;
- }
- fillLocRib(tx, entry, value, routeId);
- fillAdjRibsOut(tx, entry, value, routeId, routePeerId);
- }
- }
+ final RouteEntry entry = e.getValue();
- private void fillLocRib(final DOMDataWriteTransaction tx, final AbstractRouteEntry entry, final NormalizedNode<?, ?> value, final PathArgument routeId) {
- final YangInstanceIdentifier writePath = this.ribSupport.routePath(this.locRibTarget.node(ROUTES_IDENTIFIER), routeId);
- if (value != null) {
- LOG.debug("Write route to LocRib {}", value);
- tx.put(LogicalDatastoreType.OPERATIONAL, writePath, value);
- } else {
- LOG.debug("Delete route from LocRib {}", entry);
- tx.delete(LogicalDatastoreType.OPERATIONAL, writePath);
- }
- }
-
- @VisibleForTesting
- private void fillAdjRibsOut(final DOMDataWriteTransaction tx, final AbstractRouteEntry entry, final NormalizedNode<?, ?> value,
- final PathArgument routeId, final PeerId routePeerId) {
- /*
- * 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.
- */
- final ContainerNode attributes = entry == null ? null : entry.attributes();
- for (final PeerRole role : PeerRole.values()) {
- if(PeerRole.Internal.equals(role)) {
+ if (!entry.selectBest(this.ourAs)) {
+ LOG.trace("Best path has not changed, continuing");
continue;
}
- final PeerExportGroup peerGroup = this.peerPolicyTracker.getPeerGroup(role);
- if (peerGroup != null) {
- final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(routePeerId, attributes);
- for (final Entry<PeerId, YangInstanceIdentifier> pid : peerGroup.getPeers()) {
- final PeerId peerDestiny = pid.getKey();
- if (!routePeerId.equals(peerDestiny) && isTableSupported(peerDestiny) && !this.cacheDisconnectedPeers.isPeerDisconnected(peerDestiny)) {
- final YangInstanceIdentifier routeTarget = getRouteTarget(pid.getValue(), routeId);
- if (value != null && effectiveAttributes != null) {
- LOG.debug("Write route {} to peers AdjRibsOut {}", value, peerDestiny);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
- tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(this.attributesIdentifier), effectiveAttributes);
- } else {
- LOG.trace("Removing {} from transaction for peer {}", routeTarget, peerDestiny);
- tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
- }
- }
- }
- }
- }
- }
-
- private boolean isTableSupported(final PeerId key) {
- if (!this.peerPolicyTracker.isTableSupported(key, this.localTablesKey)) {
- LOG.trace("Route rejected, peer {} does not support this table type {}", key, this.localTablesKey);
- return false;
+ entry.updateRoute(this.localTablesKey, this.peerPolicyTracker, this.locRibTarget, this.ribSupport, this.cacheDisconnectedPeers,
+ tx, e.getKey().getRouteId());
}
- return true;
}
}
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
+import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
private final BGPConfigModuleTracker configModuleTracker;
private final BGPOpenConfigProvider openConfigProvider;
private final CacheDisconnectedPeers cacheDisconnectedPeers;
+ private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
- final GeneratedClassLoadingStrategy classStrategy, final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider) {
+ @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies, final GeneratedClassLoadingStrategy classStrategy,
+ final BGPConfigModuleTracker moduleTracker, final BGPOpenConfigProvider openConfigProvider) {
super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
this.domChain = domDataBroker.createTransactionChain(this);
this.localAs = Preconditions.checkNotNull(localAs);
this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
this.localTables = ImmutableSet.copyOf(localTables);
- this.localTablesKeys = new HashSet<TablesKey>();
+ this.localTablesKeys = new HashSet<>();
this.dataBroker = dps;
this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
this.extensions = Preconditions.checkNotNull(extensions);
this.configModuleTracker = moduleTracker;
this.openConfigProvider = openConfigProvider;
this.cacheDisconnectedPeers = new CacheDisconnectedPeersImpl();
+ this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
LOG.debug("Instantiating RIB table {} at {}", ribId, this.yangRibId);
public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
- final GeneratedClassLoadingStrategy classStrategy) {
+ final Map<TablesKey, PathSelectionMode> bestPathSelectionstrategies, final GeneratedClassLoadingStrategy classStrategy) {
this(ribId, localAs, localBgpId, clusterId, extensions, dispatcher, tcpStrategyFactory, codecFactory, sessionStrategyFactory,
- dps, domDataBroker, localTables, classStrategy, null, null);
+ dps, domDataBroker, localTables, bestPathSelectionstrategies, classStrategy, null, null);
}
private void startLocRib(final TablesKey key, final PolicyDatabase pd) {
} catch (final TransactionCommitFailedException e1) {
LOG.error("Failed to initiate LocRIB for key {}", key, e1);
}
+
+ PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
+ if (pathSelectionStrategy == null) {
+ pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
+ }
this.locRibs.add(LocRibWriter.create(this.ribContextRegistry, key, createPeerChain(this), getYangRibId(), this.localAs, getService(), pd,
- this.cacheDisconnectedPeers));
+ this.cacheDisconnectedPeers, pathSelectionStrategy));
}
@Override
--- /dev/null
+/*
+ * Copyright (c) 2016 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.spi;
+
+import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
+
+public interface BGPBestPathSelection extends BgpTableType, AutoCloseable {
+ PathSelectionMode getStrategy();
+}
import odl-tcpmd5-netty-cfg { prefix tcpmd5n; revision-date 2014-04-27; }
import rpc-context { prefix rpcx; revision-date 2013-06-17; }
import odl-bgp-openconfig-spi-cfg { prefix bgp-oc-spi; revision-date 2015-09-25; }
+ import odl-bgp-path-selection-mode { prefix bps; revision-date 2016-03-01;}
organization "Cisco Systems, Inc.";
identity bgp-peer-acceptor {
description
- "BGP peer acceptor that handles incomming bgp connections. Uses BGP peer registry to accept or decline incomming connections";
+ "BGP peer acceptor that handles incoming bgp connections. Uses BGP peer registry to accept or decline incoming connections";
base config:module-type;
config:java-name-prefix BGPPeerAcceptor;
}
}
+ identity bgp-path-selection-mode {
+ description
+ "Service representing an address family + path selection mode.";
+
+ base "config:service-type";
+ config:java-class "org.opendaylight.protocol.bgp.rib.impl.spi.BGPBestPathSelection";
+ }
+
+ identity bgp-psm-impl {
+ base config:module-type;
+ config:provided-service bgp-path-selection-mode;
+ config:java-name-prefix BGPPSMImpl;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case bgp-psm-impl {
+ when "/config:modules/config:module/config:type = 'bgp-psm-impl'";
+ container path-address-family {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity bgp-table-type;
+ }
+ }
+ }
+ container path-selection-mode {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity bps:path-selection-mode-factory;
+ }
+ }
+ }
+ }
+ }
+
identity rib-impl {
base config:module-type;
config:provided-service bgprib:rib;
reference "https://tools.ietf.org/html/rfc4456#section-7";
type inet:ipv4-address;
}
+
+ list path-selection-mode {
+ uses config:service-ref {
+ refine type {
+ config:required-identity bgp-path-selection-mode;
+ }
+ }
+ }
}
}
}
import com.google.common.util.concurrent.CheckedFuture;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.mode.impl.base.BasePathSelectionModeFactory;
import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderContext;
this.codecFactory = createCodecFactory(strategy,schemaContext);
final List<BgpTableType> localTables = new ArrayList<>();
localTables.add(new BgpTableTypeImpl(AFI, SAFI));
+
this.a1 = new RIBActivator();
this.a1.startRIBExtensionProvider(context);
mockedMethods();
this.rib = new RIBImpl(new RibId("test"), new AsNumber(5L), this.ribId,
- this.clusterId, context , this.dispatcher, this.tcpStrategyFactory, this.codecFactory, this.tcpStrategyFactory, this.dps, this.dom, localTables, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
+ this.clusterId, context , this.dispatcher, this.tcpStrategyFactory, this.codecFactory, this.tcpStrategyFactory, this.dps, this.dom,
+ localTables, Collections.singletonMap(new TablesKey(AFI, SAFI), BasePathSelectionModeFactory.createBestPathSelectionStrategy()),
+ GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
this.rib.onGlobalContextUpdated(schemaContext);
this.ribSupport = getRib().getRibSupportContext().getRIBSupportContext(KEY).getRibSupport();
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.ATTRS_EXTENSION_Q;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SEGMENTS_NID;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SEQ_SEGMENT;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SET_SEGMENT;
+
import java.util.Collection;
import java.util.Iterator;
import org.junit.Test;
public class AttributeOperationsTest {
- static final NodeIdentifier ORIGIN_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, Origin.QNAME.getLocalName()).intern());
- static final NodeIdentifier ORIGIN_VALUE_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "value").intern());
- static final NodeIdentifier AS_PATH_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, AsPath.QNAME.getLocalName()).intern());
- static final NodeIdentifier ATOMIC_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, AtomicAggregate.QNAME.getLocalName()));
- static final NodeIdentifier CLUSTER_C_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, ClusterId.QNAME.getLocalName()));
- static final NodeIdentifier CLUSTER_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster"));
- static final NodeIdentifier ORIGINATOR_C_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, OriginatorId.QNAME.getLocalName()));
- static final NodeIdentifier ORIGINATOR_NID = new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "originator"));
+ static final NodeIdentifier ORIGIN_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, Origin.QNAME.getLocalName()).intern());
+ static final NodeIdentifier ORIGIN_VALUE_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "value").intern());
+ static final NodeIdentifier AS_PATH_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, AsPath.QNAME.getLocalName()).intern());
+ static final NodeIdentifier ATOMIC_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, AtomicAggregate.QNAME.getLocalName()));
+ static final NodeIdentifier CLUSTER_C_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, ClusterId.QNAME.getLocalName()));
+ static final NodeIdentifier CLUSTER_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "cluster"));
+ static final NodeIdentifier ORIGINATOR_C_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, OriginatorId.QNAME.getLocalName()));
+ static final NodeIdentifier ORIGINATOR_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "originator"));
@Test
public void testExportedAttributesSetFirst() {
final Long ourAs = 72L;
- final ContainerNode attributesSetBefore = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BestPathSelectorTest.ATTRS_EXTENSION_Q))
+ final ContainerNode attributesSetBefore = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ATTRS_EXTENSION_Q))
.addChild(Builders.containerBuilder().withNodeIdentifier(AS_PATH_NID)
- .addChild(Builders.unkeyedListBuilder().withNodeIdentifier(BestPathSelectorTest.SEGMENTS_NID)
- .addChild(BestPathSelectorTest.SET_SEGMENT)
- .addChild(BestPathSelectorTest.SEQ_SEGMENT)
+ .addChild(Builders.unkeyedListBuilder().withNodeIdentifier(SEGMENTS_NID)
+ .addChild(SET_SEGMENT)
+ .addChild(SEQ_SEGMENT)
.build())
.build())
.build();
@Test
public void testExportedAttributesListFirst() {
final Long ourAs = 72L;
- final ContainerNode attributesListBefore = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BestPathSelectorTest.ATTRS_EXTENSION_Q))
+ final ContainerNode attributesListBefore = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ATTRS_EXTENSION_Q))
.addChild(Builders.containerBuilder().withNodeIdentifier(AS_PATH_NID)
- .addChild(Builders.unkeyedListBuilder().withNodeIdentifier(BestPathSelectorTest.SEGMENTS_NID)
- .addChild(BestPathSelectorTest.SEQ_SEGMENT)
- .addChild(BestPathSelectorTest.SET_SEGMENT)
+ .addChild(Builders.unkeyedListBuilder().withNodeIdentifier(SEGMENTS_NID)
+ .addChild(SEQ_SEGMENT)
+ .addChild(SET_SEGMENT)
.build())
.build())
.build();
@Test
public void testExportedAttributesEmptyWithTransitive() {
final Long ourAs = 72L;
- final ContainerNode attributesSetBefore = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BestPathSelectorTest.ATTRS_EXTENSION_Q))
+ final ContainerNode attributesSetBefore = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ATTRS_EXTENSION_Q))
.addChild(Builders.containerBuilder().withNodeIdentifier(ORIGIN_NID)
.addChild(Builders.leafBuilder().withNodeIdentifier(ORIGIN_VALUE_NID).withValue(BgpOrigin.Egp).build())
.build())
.addChild(Builders.containerBuilder().withNodeIdentifier(AS_PATH_NID)
- .addChild(Builders.unkeyedListBuilder().withNodeIdentifier(BestPathSelectorTest.SEGMENTS_NID).build())
+ .addChild(Builders.unkeyedListBuilder().withNodeIdentifier(SEGMENTS_NID).build())
.build())
.addChild(Builders.containerBuilder().withNodeIdentifier(ATOMIC_NID).build())
.build();
}
private LeafSetNode<?> checkFirstLeafList(final ContainerNode exportedAttributes) {
- assertTrue(NormalizedNodes.findNode(exportedAttributes, AS_PATH_NID, BestPathSelectorTest.SEGMENTS_NID).isPresent());
- final UnkeyedListNode segments = (UnkeyedListNode) NormalizedNodes.findNode(exportedAttributes, AS_PATH_NID, BestPathSelectorTest.SEGMENTS_NID).get();
+ assertTrue(NormalizedNodes.findNode(exportedAttributes, AS_PATH_NID, SEGMENTS_NID).isPresent());
+ final UnkeyedListNode segments = (UnkeyedListNode) NormalizedNodes.findNode(exportedAttributes, AS_PATH_NID, SEGMENTS_NID).get();
final UnkeyedListEntryNode seg = segments.getValue().iterator().next();
final DataContainerChild<? extends PathArgument, ?> firstLeafList = seg.getValue().iterator().next();
return (LeafSetNode<?>) firstLeafList;
public void testReflectedAttributesOriginatorAndClusterNotPresent() {
final Ipv4Address originatorId = new Ipv4Address("127.0.0.2");
final ClusterIdentifier clusterId = new ClusterIdentifier("10.10.10.10");
- final ContainerNode attributes = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BestPathSelectorTest.ATTRS_EXTENSION_Q))
+ final ContainerNode attributes = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ATTRS_EXTENSION_Q))
.addChild(Builders.containerBuilder().withNodeIdentifier(ORIGIN_NID)
.addChild(Builders.leafBuilder().withNodeIdentifier(ORIGIN_VALUE_NID).withValue(BgpOrigin.Egp).build())
.build())
final ClusterIdentifier ourClusterId = new ClusterIdentifier("1.1.1.1");
final ClusterIdentifier clusterId1 = new ClusterIdentifier("10.10.10.10");
final ClusterIdentifier clusterId2 = new ClusterIdentifier("11.11.11.11");
- final ContainerNode attributes = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BestPathSelectorTest.ATTRS_EXTENSION_Q))
+ final ContainerNode attributes = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ATTRS_EXTENSION_Q))
.addChild(Builders.containerBuilder().withNodeIdentifier(ORIGINATOR_C_NID)
.addChild(Builders.leafBuilder().withNodeIdentifier(ORIGINATOR_NID).withValue("127.0.0.2").build())
.build())
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.AS_NUMBER_Q;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.ATTRS_EXTENSION_Q;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SEGMENTS_NID;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SEQ_LEAFLIST_NID;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SEQ_SEGMENT;
+import static org.opendaylight.protocol.bgp.mode.impl.base.BestPathSelectorTest.SET_SEGMENT;
import org.junit.Test;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
*/
private static ContainerNode createInputWithOriginator() {
final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
- b.withNodeIdentifier(new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "attribute-container").intern()));
+ b.withNodeIdentifier(new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "attribute-container").intern()));
b.withChild(createClusterId());
b.withChild(createOriginatorId());
return b.build();
private static ContainerNode createInternalOutput() {
final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
- b.withNodeIdentifier(new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "attribute-container").intern()));
+ b.withNodeIdentifier(new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "attribute-container").intern()));
b.withChild(createWithoutInternalClusterId());
return b.build();
}
private static ContainerNode createClusterInput() {
final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
- b.withNodeIdentifier(new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "attribute-container").intern()));
+ b.withNodeIdentifier(new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "attribute-container").intern()));
b.withChild(createClusterIdInput());
return b.build();
}
private static ContainerNode createWithoutInternalClusterId() {
- final QName clusterContQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster-id").intern();
- final QName clusterQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster").intern();
+ final QName clusterContQName = QName.create(ATTRS_EXTENSION_Q, "cluster-id").intern();
+ final QName clusterQName = QName.create(ATTRS_EXTENSION_Q, "cluster").intern();
final NodeIdentifier clusterContNid = new NodeIdentifier(clusterContQName);
final NodeIdentifier clusterNid = new NodeIdentifier(clusterQName);
}
private static ContainerNode createClusterId() {
- final QName clusterContQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster-id").intern();
- final QName clusterQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster").intern();
+ final QName clusterContQName = QName.create(ATTRS_EXTENSION_Q, "cluster-id").intern();
+ final QName clusterQName = QName.create(ATTRS_EXTENSION_Q, "cluster").intern();
final NodeIdentifier clusterContNid = new NodeIdentifier(clusterContQName);
final NodeIdentifier clusterNid = new NodeIdentifier(clusterQName);
}
private static ContainerNode createClusterIdInput() {
- final QName clusterContQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster-id").intern();
- final QName clusterQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "cluster").intern();
+ final QName clusterContQName = QName.create(ATTRS_EXTENSION_Q, "cluster-id").intern();
+ final QName clusterQName = QName.create(ATTRS_EXTENSION_Q, "cluster").intern();
final NodeIdentifier clusterContNid = new NodeIdentifier(clusterContQName);
final NodeIdentifier clusterNid = new NodeIdentifier(clusterQName);
}
private static ContainerNode createOriginatorId() {
- final QName originatorContQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "originator-id").intern();
- final QName originatorQName = QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "originator").intern();
+ final QName originatorContQName = QName.create(ATTRS_EXTENSION_Q, "originator-id").intern();
+ final QName originatorQName = QName.create(ATTRS_EXTENSION_Q, "originator").intern();
final NodeIdentifier originatorContNid = new NodeIdentifier(originatorContQName);
final NodeIdentifier originatorNid = new NodeIdentifier(originatorQName);
private static ContainerNode createPathInput(final UnkeyedListEntryNode child) {
final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> segB = Builders.unkeyedListBuilder();
- segB.withNodeIdentifier(BestPathSelectorTest.SEGMENTS_NID);
+ segB.withNodeIdentifier(SEGMENTS_NID);
if (child != null) {
segB.addChild(child);
}
- segB.addChild(BestPathSelectorTest.SET_SEGMENT).addChild(BestPathSelectorTest.SEQ_SEGMENT);
- return Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BestPathSelectorTest.ATTRS_EXTENSION_Q))
- .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(QName.create(BestPathSelectorTest.ATTRS_EXTENSION_Q, "as-path").intern()))
+ segB.addChild(SET_SEGMENT).addChild(SEQ_SEGMENT);
+ return Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ATTRS_EXTENSION_Q))
+ .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-path").intern()))
.addChild(segB.build()).build()).build();
}
private static UnkeyedListEntryNode createSequenceWithLocalAs() {
- return Builders.unkeyedListEntryBuilder().withNodeIdentifier(BestPathSelectorTest.SEGMENTS_NID)
- .addChild(Builders.orderedLeafSetBuilder().withNodeIdentifier(BestPathSelectorTest.SEQ_LEAFLIST_NID)
- .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(BestPathSelectorTest.AS_NUMBER_Q, LOCAL_AS)).withValue(LOCAL_AS).build())
+ return Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
+ .addChild(Builders.orderedLeafSetBuilder().withNodeIdentifier(SEQ_LEAFLIST_NID)
+ .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, LOCAL_AS)).withValue(LOCAL_AS).build())
.build()).build();
}
}
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.mode.impl.base.BasePathSelectionModeFactory;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
Mockito.doReturn(null).when(this.service).registerDataTreeChangeListener(Mockito.any(DOMDataTreeIdentifier.class), Mockito.any(DOMDataTreeChangeListener.class));
Mockito.doReturn(Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(Routes.QNAME)).addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(Ipv4Routes.QNAME)).withChild(ImmutableNodes.mapNodeBuilder(Ipv4Route.QNAME).build()).build()).build()).when(this.ribSupport).emptyRoutes();
this.locRibWriter = LocRibWriter.create(this.registry, this.tablesKey, this.chain, YangInstanceIdentifier.of(BgpRib.QNAME),
- new AsNumber((long) 35), this.service, this.pd, new CacheDisconnectedPeersImpl());
+ new AsNumber((long) 35), this.service, this.pd, new CacheDisconnectedPeersImpl(), BasePathSelectionModeFactory.createBestPathSelectionStrategy());
}
private DataTreeCandidate prepareUpdate() {
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.binding.test.DataBrokerTestCustomizer;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
public void testWithLinkstate() throws InterruptedException, ExecutionException {
final List<BgpTableType> tables = ImmutableList.of(
(BgpTableType) new BgpTableTypeImpl(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class));
- final RIBImpl rib = new RIBImpl(new RibId(TEST_RIB_ID), new AsNumber(72L), new Ipv4Address("127.0.0.1"), null, this.ext2, this.dispatcher, this.tcpStrategyFactory, this.codecFactory, this.sessionStrategy, getDataBroker(), getDomBroker(), tables, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
+ final RIBImpl rib = new RIBImpl(new RibId(TEST_RIB_ID), new AsNumber(72L), new Ipv4Address("127.0.0.1"), null, this.ext2, this.dispatcher,
+ this.tcpStrategyFactory, this.codecFactory, this.sessionStrategy, getDataBroker(), getDomBroker(), tables,
+ Collections.singletonMap(new TablesKey(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class),
+ BasePathSelectionModeFactory.createBestPathSelectionStrategy()), GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
assertTablesExists(tables, true);
rib.onGlobalContextUpdated(this.schemaService.getGlobalContext());
final BGPPeer peer = new BGPPeer("peer-" + this.mock.toString(), rib);
@Test
public void testWithoutLinkstate() throws InterruptedException, ExecutionException {
final List<BgpTableType> tables = ImmutableList.of((BgpTableType) new BgpTableTypeImpl(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class));
- final RIBImpl rib = new RIBImpl(new RibId(TEST_RIB_ID), new AsNumber(72L), new Ipv4Address("127.0.0.1"), null, this.ext1, this.dispatcher, this.tcpStrategyFactory, this.codecFactory, this.sessionStrategy, getDataBroker(), getDomBroker(), tables, GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
+ final RIBImpl rib = new RIBImpl(new RibId(TEST_RIB_ID), new AsNumber(72L), new Ipv4Address("127.0.0.1"), null, this.ext1, this.dispatcher,
+ this.tcpStrategyFactory, this.codecFactory, this.sessionStrategy, getDataBroker(), getDomBroker(), tables,
+ Collections.singletonMap(new TablesKey(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class),
+ BasePathSelectionModeFactory.createBestPathSelectionStrategy()), GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy());
rib.onGlobalContextUpdated(this.schemaService.getGlobalContext());
assertTablesExists(tables, true);
final BGPPeer peer = new BGPPeer("peer-" + this.mock.toString(), rib);
import org.junit.Test;
import org.opendaylight.protocol.bgp.parser.BGPError;
-import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
public class TerminationReasonTest {
<groupId>${project.groupId}</groupId>
<artifactId>bgp-openconfig-impl</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bgp-path-selection-mode</artifactId>
+ </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<feature version='${project.version}'>odl-bgpcep-bgp-flowspec</feature>
<feature version='${project.version}'>odl-bgpcep-bgp-labeled-unicast</feature>
<feature version='${project.version}'>odl-bgpcep-bgp-l3vpn</feature>
+ <feature version='${project.version}'>odl-bgpcep-bgp-path-selection-mode</feature>
<feature version='${project.version}'>odl-bgpcep-bgp-topology</feature>
<feature version='${project.version}'>odl-bgpcep-bgp-openconfig</feature>
<configfile finalname="etc/opendaylight/karaf/31-bgp.xml">mvn:org.opendaylight.bgpcep/bgp-controller-config/{{VERSION}}/xml/config</configfile>
<bundle>mvn:org.opendaylight.bgpcep/bgp-bmp-api/{{VERSION}}</bundle>
</feature>
+ <feature name='odl-bgpcep-bgp-path-selection-mode' version='${project.version}'>
+ <feature version='${project.version}'>odl-bgpcep-bgp-dependencies</feature>
+ <feature version='${config.version}'>odl-config-api</feature>
+ <bundle>mvn:org.opendaylight.bgpcep/bgp-parser-api/{{VERSION}}</bundle>
+ <feature version='${project.version}'>odl-bgpcep-bgp-rib-api</feature>
+ <bundle>mvn:org.opendaylight.bgpcep/bgp-path-selection-mode/{{VERSION}}</bundle>
+ </feature>
+
<feature name='odl-bgpcep-bgp-rib-impl' version='${project.version}'>
<feature version='${project.version}'>odl-bgpcep-bgp-dependencies</feature>
<feature version='${tcpmd5.version}'>odl-tcpmd5-netty</feature>
<feature version='${mdsal.version}'>odl-mdsal-broker</feature>
<feature version='${project.version}'>odl-bgpcep-bgp-linkstate</feature>
<feature version='${project.version}'>odl-bgpcep-bgp-rib-api</feature>
+ <feature version='${project.version}'>odl-bgpcep-bgp-path-selection-mode</feature>
<bundle>mvn:org.opendaylight.bgpcep/bgp-openconfig-spi/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.bgpcep/bgp-rib-impl/{{VERSION}}</bundle>
</feature>