BUG-4827: Path Selection mode Config Subsystem integration 52/36052/10
authorClaudio D. Gasparini <cgaspari@cisco.com>
Sat, 5 Mar 2016 21:13:42 +0000 (22:13 +0100)
committerMilos Fabian <milfabia@cisco.com>
Wed, 16 Mar 2016 23:44:12 +0000 (23:44 +0000)
Create new module for path selection mode allowing us to
integrate add paths selections mode  in a close future.

Change-Id: I36ec3e8267137505f76d6ee7230d9ce9aeea2714
Signed-off-by: Claudio D. Gasparini <cgaspari@cisco.com>
39 files changed:
artifacts/pom.xml
bgp/path-selection-mode/.project [new file with mode: 0644]
bgp/path-selection-mode/pom.xml [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/BestPath.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/BestPathState.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/PathSelectionMode.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/RouteEntry.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/BestPathStateImpl.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BestPathState.java with 94% similarity]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseAbstractRouteEntry.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseBestPath.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BestPath.java with 68% similarity]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseComplexRouteEntry.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ComplexRouteEntry.java with 71% similarity]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelection.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelectionModeFactory.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelector.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BestPathSelector.java with 93% similarity]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseSimpleRouteEntry.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/SimpleRouteEntry.java with 75% similarity]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/OffsetMap.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/OffsetMap.java with 95% similarity]
bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/spi/AbstractBestPath.java [new file with mode: 0644]
bgp/path-selection-mode/src/main/yang/bgp-path-selection-mode.yang [new file with mode: 0644]
bgp/path-selection-mode/src/test/java/org/opendaylight/protocol/bgp/mode/impl/base/BestPathSelectorTest.java [moved from bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/BestPathSelectorTest.java with 87% similarity]
bgp/path-selection-mode/src/test/java/org/opendaylight/protocol/bgp/mode/impl/base/OffsetMapTest.java [moved from bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/OffsetMapTest.java with 92% similarity]
bgp/pom.xml
bgp/rib-impl/pom.xml
bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/AddPathImplModule.java
bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPSMImplModule.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPSMImplModuleFactory.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/RIBImplModule.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractRouteEntry.java [deleted file]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RIBImpl.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPBestPathSelection.java [new file with mode: 0644]
bgp/rib-impl/src/main/yang/odl-bgp-rib-impl-cfg.yang
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AbstractRIBTestSetup.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AttributeOperationsTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriterTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/ParserToSalTest.java
bgp/rib-spi/src/test/java/org/opendaylight/protocol/bgp/rib/spi/TerminationReasonTest.java
features/bgp/pom.xml
features/bgp/src/main/features/features.xml

index fdb450e2a01976cc639d3f4a581906127fa88e34..f0d789cdada0e2e682862cb70bcfc4a55d25faec 100644 (file)
                 <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>
diff --git a/bgp/path-selection-mode/.project b/bgp/path-selection-mode/.project
new file mode 100644 (file)
index 0000000..8b344c9
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>
diff --git a/bgp/path-selection-mode/pom.xml b/bgp/path-selection-mode/pom.xml
new file mode 100644 (file)
index 0000000..1e6e827
--- /dev/null
@@ -0,0 +1,108 @@
+<?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>
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/BestPath.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/BestPath.java
new file mode 100644 (file)
index 0000000..0f9bb05
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/BestPathState.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/BestPathState.java
new file mode 100644 (file)
index 0000000..6b8bfb0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/PathSelectionMode.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/PathSelectionMode.java
new file mode 100644 (file)
index 0000000..4e7b2a5
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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);
+}
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/RouteEntry.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/api/RouteEntry.java
new file mode 100644 (file)
index 0000000..726e538
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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
similarity index 94%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BestPathState.java
rename to bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/BestPathStateImpl.java
index 68bcc2b159504cf71710c3fdeec22171cac36e24..6a87320ba052ec0f8d5ae94f7396898a3e76ec98 100644 (file)
@@ -5,9 +5,8 @@
  * 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;
@@ -21,6 +20,7 @@ import java.util.List;
 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;
@@ -45,7 +45,7 @@ import org.slf4j.Logger;
 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;
@@ -100,7 +100,7 @@ final class BestPathState {
         }
     }
 
-    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;
@@ -113,7 +113,7 @@ final class BestPathState {
     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>() {
@@ -175,7 +175,7 @@ final class BestPathState {
         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);
             }
@@ -183,27 +183,32 @@ final class BestPathState {
         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;
     }
@@ -235,7 +240,6 @@ final class BestPathState {
         return new AsNumber(0L);
     }
 
-    @VisibleForTesting
     public List<Segments> extractSegments(final UnkeyedListNode segments) {
         // list segments
         final List<Segments> extracted = new ArrayList<>();
@@ -261,7 +265,8 @@ final class BestPathState {
         return null;
     }
 
-    ContainerNode getAttributes() {
+    @Override
+    public ContainerNode getAttributes() {
         return this.attributes;
     }
 
@@ -295,10 +300,10 @@ final class BestPathState {
         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;
         }
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseAbstractRouteEntry.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseAbstractRouteEntry.java
new file mode 100644 (file)
index 0000000..bc4b6c6
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * 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
similarity index 68%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BestPath.java
rename to bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseBestPath.java
index 5607b2515235b651b8171eb868b56ea0fd6f14b3..f34cdec7443d95dbbae2199c38e835bda603ef49 100644 (file)
@@ -5,29 +5,33 @@
  * 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) {
@@ -55,10 +59,10 @@ final class BestPath {
         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;
         }
similarity index 71%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ComplexRouteEntry.java
rename to bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseComplexRouteEntry.java
index eb67f79fc61519edf6b766e728be3376d550eedb..da245c810d6b6fc574da022af1c9a5b830d42457 100644 (file)
@@ -5,22 +5,23 @@
  * 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)) {
@@ -32,14 +33,14 @@ final class ComplexRouteEntry extends AbstractRouteEntry {
     }
 
     @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);
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelection.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelection.java
new file mode 100644 (file)
index 0000000..c39c2e1
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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
+    }
+}
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelectionModeFactory.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelectionModeFactory.java
new file mode 100644 (file)
index 0000000..eef4158
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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();
+    }
+}
similarity index 93%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BestPathSelector.java
rename to bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BasePathSelector.java
index fa8479d7e1b1830b8babf490d686c2790ca2f3f5..9af9766819960d90202cc70284b86ec6d613a5d8 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
@@ -13,6 +13,8 @@ import com.google.common.collect.ImmutableList;
 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;
@@ -26,8 +28,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
 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;
@@ -35,7 +37,7 @@ final class BestPathSelector {
     private UnsignedInteger bestRouterId = null;
     private BestPathState bestState = null;
 
-    BestPathSelector(final Long ourAs) {
+    BasePathSelector(final Long ourAs) {
         this.ourAs = Preconditions.checkNotNull(ourAs);
     }
 
@@ -60,7 +62,7 @@ final class BestPathSelector {
              * 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;
@@ -70,8 +72,8 @@ final class BestPathSelector {
         }
     }
 
-    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);
     }
 
     /**
similarity index 75%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/SimpleRouteEntry.java
rename to bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/BaseSimpleRouteEntry.java
index 1dd90c808202cb78bd1db5c357ed636487e6d3a0..e26653cd5495a13b63a36db4daa4078a876dc014 100644 (file)
@@ -5,26 +5,27 @@
  * 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();
     }
 }
similarity index 95%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/OffsetMap.java
rename to bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/impl/base/OffsetMap.java
index 33b2db4b7674917e6838dd401b4e50da23136ade..adfd19ca127c13cb2d75ecae881d3154e7d43264 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
@@ -24,15 +24,14 @@ import org.slf4j.LoggerFactory;
 
 /**
  * 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 {
@@ -48,7 +47,7 @@ final class OffsetMap {
     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;
     }
@@ -66,6 +65,10 @@ final class OffsetMap {
         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
diff --git a/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/spi/AbstractBestPath.java b/bgp/path-selection-mode/src/main/java/org/opendaylight/protocol/bgp/mode/spi/AbstractBestPath.java
new file mode 100644 (file)
index 0000000..1bab342
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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
diff --git a/bgp/path-selection-mode/src/main/yang/bgp-path-selection-mode.yang b/bgp/path-selection-mode/src/main/yang/bgp-path-selection-mode.yang
new file mode 100644 (file)
index 0000000..7ab9331
--- /dev/null
@@ -0,0 +1,36 @@
+// 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
similarity index 87%
rename from bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/BestPathSelectorTest.java
rename to bgp/path-selection-mode/src/test/java/org/opendaylight/protocol/bgp/mode/impl/base/BestPathSelectorTest.java
index 036e201b43c91fe4788182c3d2e7233a6af02999..5b7293443af9fabbe6434e9a1a1149b4aef12b41 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
@@ -16,6 +16,7 @@ import com.google.common.primitives.UnsignedInteger;
 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;
@@ -37,7 +38,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUn
 
 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");
@@ -45,30 +46,30 @@ public class BestPathSelectorTest {
     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())
@@ -78,7 +79,7 @@ public class BestPathSelectorTest {
     @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());
@@ -91,7 +92,7 @@ public class BestPathSelectorTest {
     @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
@@ -107,7 +108,7 @@ public class BestPathSelectorTest {
     @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
@@ -146,9 +147,9 @@ public class BestPathSelectorTest {
     @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());
@@ -238,7 +239,7 @@ public class BestPathSelectorTest {
         // 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));
     }
@@ -247,12 +248,12 @@ public class BestPathSelectorTest {
     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();
     }
 }
similarity index 92%
rename from bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/OffsetMapTest.java
rename to bgp/path-selection-mode/src/test/java/org/opendaylight/protocol/bgp/mode/impl/base/OffsetMapTest.java
index 32616d5893dd51ddf8fd5029c7aa4a3b21c10edd..9b546f2c768f57bb70099dc029c503acabde181b 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
@@ -17,8 +17,8 @@ public class OffsetMapTest {
 
     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;
index 80a3dbce353caa42608103895cedd1d6fda8781b..e78b9ccbf067acb6ab894254547b3f707504256d 100644 (file)
@@ -55,5 +55,6 @@
         <module>openconfig-api</module>
         <module>openconfig-spi</module>
         <module>openconfig-impl</module>
+        <module>path-selection-mode</module>
   </modules>
 </project>
index 916ccf10f4c2c5d3520660fb864b8fcad563529b..3f0d91bd1ecd7c5bfa5c611b11034672ab70058a 100644 (file)
             <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>
index 6401bca0b6668e68e00e4cbf939693dca21ee502..76eaf9ffb906cb3e698555e55b0ece3419507304 100644 (file)
@@ -17,7 +17,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.type
 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);
     }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPSMImplModule.java b/bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPSMImplModule.java
new file mode 100644 (file)
index 0000000..32d5860
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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());
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPSMImplModuleFactory.java b/bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPSMImplModuleFactory.java
new file mode 100644 (file)
index 0000000..0d2c403
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * 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 {
+
+}
index 1f2dca1f4cabd1d2ad9f1c0a45f7f5017a7122e0..c64061cb9f0918f2e37dbc95d11bc251ac20391c 100644 (file)
  */
 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;
@@ -66,9 +72,10 @@ public final class RIBImplModule extends org.opendaylight.controller.config.yang
     @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;
@@ -89,6 +96,11 @@ public final class RIBImplModule extends org.opendaylight.controller.config.yang
         }
     }
 
+    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;
     }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractRouteEntry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractRouteEntry.java
deleted file mode 100644 (file)
index a8dd654..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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);
-}
index f52824caded76b9cd5f2295a78b3718a23c9226e..e64ea404cf2154b615bb3553315fd83190998548 100644 (file)
@@ -7,14 +7,12 @@
  */
 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;
@@ -23,6 +21,8 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.protocol.bgp.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;
@@ -36,7 +36,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.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;
@@ -49,7 +48,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
@@ -65,11 +63,10 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     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;
@@ -80,9 +77,11 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     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;
@@ -92,6 +91,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
         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());
@@ -103,9 +103,10 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
         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
@@ -116,8 +117,8 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     }
 
     @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;
@@ -133,7 +134,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
              * 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);
@@ -144,8 +145,8 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
         }
     }
 
-    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();
@@ -161,7 +162,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     }
 
     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());
@@ -181,7 +182,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
         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);
             }
         }
@@ -189,30 +190,15 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
 
     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) {
@@ -224,7 +210,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
     }
 
     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())) {
@@ -239,15 +225,13 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
         }
     }
 
-    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);
@@ -256,92 +240,24 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener {
             } 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;
     }
 }
index 03d965460e6794f20e855ba110852ea013972059..c5336d6f57eb63b5913fb9bb7975a4b33be247f5 100644 (file)
@@ -16,9 +16,11 @@ import com.google.common.collect.ImmutableSet;
 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;
@@ -33,6 +35,8 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
 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;
@@ -107,11 +111,13 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
     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);
@@ -120,7 +126,7 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
         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);
@@ -131,6 +137,7 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
         this.configModuleTracker = moduleTracker;
         this.openConfigProvider = openConfigProvider;
         this.cacheDisconnectedPeers = new CacheDisconnectedPeersImpl();
+        this.bestPathSelectionStrategies = Preconditions.checkNotNull(bestPathSelectionStrategies);
 
         LOG.debug("Instantiating RIB table {} at {}", ribId, this.yangRibId);
 
@@ -180,9 +187,9 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
     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) {
@@ -210,8 +217,13 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
         } 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
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPBestPathSelection.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPBestPathSelection.java
new file mode 100644 (file)
index 0000000..0543225
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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();
+}
index 0bda338416a08d99de40335c8e6b8be97c102d9b..d00e6fde1907218af684b95580a7ae8ab70b8e71 100644 (file)
@@ -20,6 +20,7 @@ module odl-bgp-rib-impl-cfg {
     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.";
 
@@ -172,7 +173,7 @@ module odl-bgp-rib-impl-cfg {
 
     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;
@@ -653,6 +654,42 @@ module odl-bgp-rib-impl-cfg {
         }
      }
 
+    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;
@@ -768,6 +805,14 @@ module odl-bgp-rib-impl-cfg {
                 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;
+                    }
+                }
+            }
         }
     }
 }
index dd96631af7b8afc17265ff1bfab675522c61946f..cde2648bf8bb53baf9c625e00dc190cb7fb8bdd0 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.base.Throwables;
 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;
@@ -36,6 +37,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.protocol.bgp.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;
@@ -139,11 +141,14 @@ public class AbstractRIBTestSetup {
         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();
     }
index 973b706ff92dceeeecefe716562276b5cd3e7946..56aa40cbb13c69779d2306d1c105939c53466cda 100644 (file)
@@ -10,6 +10,11 @@ package org.opendaylight.protocol.bgp.rib.impl;
 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;
@@ -36,23 +41,23 @@ import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 
 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();
@@ -67,11 +72,11 @@ public class AttributeOperationsTest {
     @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();
@@ -90,12 +95,12 @@ public class AttributeOperationsTest {
     @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();
@@ -115,8 +120,8 @@ public class AttributeOperationsTest {
     }
 
     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;
@@ -126,7 +131,7 @@ public class AttributeOperationsTest {
     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())
@@ -153,7 +158,7 @@ public class AttributeOperationsTest {
         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())
index 72e771b9fab573098ff80d7426cb598d842bc5c9..46eb3e5324d76f53e45d996864dbc423202922d7 100644 (file)
@@ -9,6 +9,12 @@ package org.opendaylight.protocol.bgp.rib.impl;
 
 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;
@@ -101,7 +107,7 @@ public class ExportPolicyTest {
      */
     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();
@@ -109,21 +115,21 @@ public class ExportPolicyTest {
 
     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);
 
@@ -136,8 +142,8 @@ public class ExportPolicyTest {
     }
 
     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);
 
@@ -152,8 +158,8 @@ public class ExportPolicyTest {
     }
 
     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);
 
@@ -167,8 +173,8 @@ public class ExportPolicyTest {
     }
 
     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);
 
@@ -185,20 +191,20 @@ public class ExportPolicyTest {
 
     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();
     }
 }
index 6b7b2daf15e2824a94f256a04dd74b7bfca1fd3e..ed21b1e5c94fbfa8ae93a79224bec852c541351a 100644 (file)
@@ -25,6 +25,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.protocol.bgp.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;
@@ -121,7 +122,7 @@ public class LocRibWriterTest {
         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() {
index 516aaabd8aeabe15c6730e45fcf030623a615319..4774dfd766307a041220f327ef7642a86d8fa3e9 100644 (file)
@@ -20,6 +20,7 @@ import io.netty.util.concurrent.GlobalEventExecutor;
 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;
@@ -33,6 +34,7 @@ import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
 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;
@@ -57,6 +59,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.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;
@@ -134,7 +137,10 @@ public class ParserToSalTest extends AbstractDataBrokerTest {
     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);
@@ -146,7 +152,10 @@ public class ParserToSalTest extends AbstractDataBrokerTest {
     @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);
index b8c5ecd6f6051019b5ee997b4bb816e0853567aa..ed62d1f68a9b29ba6bf31a72a81213d48bf64283 100644 (file)
@@ -11,7 +11,6 @@ import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
 import org.opendaylight.protocol.bgp.parser.BGPError;
-import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
 
 public class TerminationReasonTest {
 
index ed2096ea83de549923c5b90231ae40a8f9a3e1e5..7464b5d88efa1ccd5a9830fb99d946dd1a5f8b0b 100644 (file)
           <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>
index b0c0b622c04c6f83bcfdee398a46fc87457296e2..8cacf59b7a0d66a28aa72d9bf284ed8ff7cd87bb 100644 (file)
@@ -35,6 +35,7 @@
         <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>