ONOS-6225 Expose k-shortest path search as part of TopologyService.

Change-Id: Idf812a707aba0b972fcfbde871c624dfc86b6e1b
diff --git a/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java b/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java
index 13d1842..76ae91e 100644
--- a/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java
+++ b/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.net.topology;
 
+import org.onlab.util.GuavaCollectors;
 import org.onosproject.event.ListenerService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -24,6 +25,7 @@
 
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Stream;
 
 /**
  * Service for providing network topology information.
@@ -130,6 +132,48 @@
                        LinkWeigher weigher);
 
     /**
+     * Returns the k-shortest paths between source and
+     * destination devices.
+     *
+     * The first {@code maxPaths} paths will be returned
+     * in ascending order according to the provided {@code weigher}
+     *
+     * @param topology topology descriptor
+     * @param src    source device
+     * @param dst    destination device
+     * @param weigher edge-weight entity
+     * @param maxPaths maximum number of paths (k)
+     * @return set of k-shortest paths
+     */
+    default Set<Path> getKShortestPaths(Topology topology,
+                                       DeviceId src, DeviceId dst,
+                                       LinkWeigher weigher,
+                                       int maxPaths) {
+        return getKShortestPaths(topology, src, dst, weigher)
+                .limit(maxPaths)
+                .collect(GuavaCollectors.toImmutableSet());
+    }
+
+    /**
+     * Returns the k-shortest paths between source and
+     * destination devices.
+     *
+     * The first {@code maxPaths} paths will be returned
+     * in ascending order according to the provided {@code weigher}
+     *
+     * @param topology topology descriptor
+     * @param src    source device
+     * @param dst    destination device
+     * @param weigher edge-weight entity
+     * @return stream of k-shortest paths
+     */
+    default Stream<Path> getKShortestPaths(Topology topology,
+                                        DeviceId src, DeviceId dst,
+                                        LinkWeigher weigher) {
+         return getPaths(topology, src, dst, weigher).stream();
+     }
+
+    /**
      * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
      * between the specified source and destination devices.
      *
diff --git a/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java b/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java
index 907beb2..d87fff0 100644
--- a/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java
+++ b/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.net.topology;
 
+import org.onlab.util.GuavaCollectors;
 import org.onosproject.event.Event;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -26,6 +27,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Stream;
 import java.util.Map;
 
 /**
@@ -129,6 +131,45 @@
                        LinkWeigher weigher);
 
     /**
+     * Computes and returns the k-shortest paths between source and
+     * destination devices.
+     *
+     * The first {@code maxPaths} paths will be returned
+     * in ascending order according to the provided {@code weigher}
+     *
+     * @param topology topology descriptor
+     * @param src    source device
+     * @param dst    destination device
+     * @param weigher edge-weight entity
+     * @param maxPaths maximum number of paths (k)
+     * @return set of k-shortest paths
+     */
+    default Set<Path> getKShortestPaths(Topology topology,
+                                        DeviceId src, DeviceId dst,
+                                        LinkWeigher weigher,
+                                        int maxPaths) {
+        return getKShortestPaths(topology, src, dst, weigher)
+                .limit(maxPaths)
+                .collect(GuavaCollectors.toImmutableSet());
+    }
+
+    /**
+     * Computes and returns the k-shortest paths between source and
+     * destination devices.
+     *
+     * @param topology topology descriptor
+     * @param src    source device
+     * @param dst    destination device
+     * @param weigher edge-weight entity
+     * @return stream of k-shortest paths
+     */
+    default Stream<Path> getKShortestPaths(Topology topology,
+                                        DeviceId src, DeviceId dst,
+                                        LinkWeigher weigher) {
+        return getPaths(topology, src, dst, weigher).stream();
+    }
+
+    /**
      * Computes and returns the set of disjoint shortest path pairs
      * between src and dst.
      *
diff --git a/core/common/src/main/java/org/onosproject/common/DefaultTopology.java b/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
index 53bebb6..dd2f3a9 100644
--- a/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
+++ b/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
@@ -27,12 +27,15 @@
 import org.onlab.graph.DisjointPathPair;
 import org.onlab.graph.GraphPathSearch;
 import org.onlab.graph.GraphPathSearch.Result;
+import org.onlab.graph.KShortestPathsSearch;
+import org.onlab.graph.LazyKShortestPathsSearch;
 import org.onlab.graph.ScalarWeight;
 import org.onlab.graph.SrlgGraphSearch;
 import org.onlab.graph.SuurballeGraphSearch;
 import org.onlab.graph.TarjanGraphSearch;
 import org.onlab.graph.TarjanGraphSearch.SccResult;
 import org.onlab.graph.Weight;
+import org.onlab.util.GuavaCollectors;
 import org.onosproject.net.AbstractModel;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultDisjointPath;
@@ -62,6 +65,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
@@ -86,6 +90,11 @@
             new TarjanGraphSearch<>();
     private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE =
             new SuurballeGraphSearch<>();
+    private static final KShortestPathsSearch<TopologyVertex, TopologyEdge> KSHORTEST =
+            new KShortestPathsSearch<>();
+    private static final LazyKShortestPathsSearch<TopologyVertex, TopologyEdge> LAZY_KSHORTEST =
+            new LazyKShortestPathsSearch<>();
+
 
     private static LinkWeigher defaultLinkWeigher = null;
     private static GraphPathSearch<TopologyVertex, TopologyEdge> defaultGraphPathSearch = null;
@@ -387,7 +396,88 @@
     }
 
     /**
-     * /**
+     * Computes on-demand the k-shortest paths between source and
+     * destination devices.
+     *
+     * @param src    source device
+     * @param dst    destination device
+     * @param maxPaths maximum number of paths (k)
+     * @return set of k-shortest paths
+     */
+    public Set<Path> getKShortestPaths(DeviceId src, DeviceId dst,
+                                       int maxPaths) {
+
+        return getKShortestPaths(src, dst, linkWeight(), maxPaths);
+    }
+
+    /**
+     * Computes on-demand the k-shortest paths between source and
+     * destination devices.
+     *
+     * The first {@code maxPaths} paths will be returned
+     * in ascending order according to the provided {@code weigher}
+     *
+     * @param src    source device
+     * @param dst    destination device
+     * @param weigher link weight function
+     * @param maxPaths maximum number of paths (k)
+     * @return set of k-shortest paths
+     */
+    public Set<Path> getKShortestPaths(DeviceId src, DeviceId dst,
+                                       LinkWeigher weigher,
+                                       int maxPaths) {
+        DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
+        DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
+        Set<TopologyVertex> vertices = graph.getVertexes();
+        if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
+            // src or dst not part of the current graph
+            return ImmutableSet.of();
+        }
+
+        return KSHORTEST.search(graph, srcV, dstV, weigher, maxPaths)
+                .paths().stream()
+                    .map(this::networkPath)
+                    .collect(GuavaCollectors.toImmutableSet());
+    }
+
+    /**
+     * Lazily computes on-demand the k-shortest paths between source and
+     * destination devices.
+     *
+     *
+     * @param src    source device
+     * @param dst    destination device
+     * @return stream of k-shortest paths
+     */
+    public Stream<Path> getKShortestPaths(DeviceId src, DeviceId dst) {
+        return getKShortestPaths(src, dst, linkWeight());
+    }
+
+    /**
+     * Lazily computes on-demand the k-shortest paths between source and
+     * destination devices.
+     *
+     *
+     * @param src    source device
+     * @param dst    destination device
+     * @param weigher link weight function
+     * @return stream of k-shortest paths
+     */
+    public Stream<Path> getKShortestPaths(DeviceId src, DeviceId dst,
+                                          LinkWeigher weigher) {
+        DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
+        DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
+        Set<TopologyVertex> vertices = graph.getVertexes();
+        if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
+            // src or dst not part of the current graph
+            return Stream.empty();
+        }
+
+        return LAZY_KSHORTEST.lazyPathSearch(graph, srcV, dstV, weigher)
+                    .map(this::networkPath);
+    }
+
+    /**
      * Returns the set of pre-computed shortest disjoint path pairs between
      * source and destination devices.
      *
diff --git a/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java b/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java
index 48403d5..481e7f7 100644
--- a/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java
+++ b/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java
@@ -40,7 +40,6 @@
 import org.onosproject.net.topology.TopologyVertex;
 
 import java.util.Set;
-
 import static com.google.common.collect.ImmutableSet.of;
 import static org.junit.Assert.*;
 import static org.onosproject.net.DeviceId.deviceId;
@@ -121,6 +120,12 @@
 
         paths = dt.getPaths(D1, D3, WEIGHER);
         assertEquals("incorrect path count", 1, paths.size());
+
+        paths = dt.getKShortestPaths(D1, D2, 42);
+        assertEquals("incorrect path count", 2, paths.size());
+
+        assertEquals("incorrect path count", 2, dt.getKShortestPaths(D1, D2).limit(42).count());
+
     }
 
     @Test
diff --git a/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java b/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java
index ecf7e25..48a8d65 100644
--- a/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java
+++ b/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java
@@ -48,6 +48,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Stream;
 import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -169,11 +170,38 @@
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
-        checkNotNull(weigher, "Link weight cannot be null");
+        checkNotNull(weigher, LINK_WEIGHT_NULL);
         return store.getPaths(topology, src, dst, weigher);
     }
 
     @Override
+    public Set<Path> getKShortestPaths(Topology topology, DeviceId src,
+                                       DeviceId dst, LinkWeigher weigher,
+                                       int maxPaths) {
+        checkPermission(TOPOLOGY_READ);
+
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(src, DEVICE_ID_NULL);
+        checkNotNull(dst, DEVICE_ID_NULL);
+        checkNotNull(weigher, LINK_WEIGHT_NULL);
+        return store.getKShortestPaths(topology, src, dst, weigher, maxPaths);
+    }
+
+    @Override
+    public Stream<Path> getKShortestPaths(Topology topology,
+                                          DeviceId src,
+                                          DeviceId dst,
+                                          LinkWeigher weigher) {
+        checkPermission(TOPOLOGY_READ);
+
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(src, DEVICE_ID_NULL);
+        checkNotNull(dst, DEVICE_ID_NULL);
+        checkNotNull(weigher, LINK_WEIGHT_NULL);
+        return store.getKShortestPaths(topology, src, dst, weigher);
+    }
+
+    @Override
     public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
                                               DeviceId dst) {
         checkPermission(TOPOLOGY_READ);
diff --git a/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java b/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java
index ffb5a95..585ed14 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java
@@ -69,6 +69,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.onlab.util.Tools.get;
@@ -226,6 +227,22 @@
     }
 
     @Override
+    public Set<Path> getKShortestPaths(Topology topology,
+                                       DeviceId src, DeviceId dst,
+                                       LinkWeigher weigher,
+                                       int maxPaths) {
+        return defaultTopology(topology).getKShortestPaths(src, dst, weigher, maxPaths);
+    }
+
+    @Override
+    public Stream<Path> getKShortestPaths(Topology topology,
+                                          DeviceId src,
+                                          DeviceId dst,
+                                          LinkWeigher weigher) {
+        return defaultTopology(topology).getKShortestPaths(src, dst, weigher);
+    }
+
+    @Override
     public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
         return defaultTopology(topology).getDisjointPaths(src, dst);
     }