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 13d18425aa..76ae91eb29 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 org.onosproject.net.Path; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; /** * Service for providing network topology information. @@ -129,6 +131,48 @@ public interface TopologyService Set getPaths(Topology topology, DeviceId src, DeviceId dst, 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 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 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 907beb21a5..d87fff01aa 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 org.onosproject.store.Store; import java.util.List; import java.util.Set; +import java.util.stream.Stream; import java.util.Map; /** @@ -128,6 +130,45 @@ public interface TopologyStore extends Store getPaths(Topology topology, DeviceId src, DeviceId dst, 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 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 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 53bebb6f74..dd2f3a9c29 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.DijkstraGraphSearch; 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.List; 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 @@ public class DefaultTopology extends AbstractModel implements Topology { new TarjanGraphSearch<>(); private static final SuurballeGraphSearch SUURBALLE = new SuurballeGraphSearch<>(); + private static final KShortestPathsSearch KSHORTEST = + new KShortestPathsSearch<>(); + private static final LazyKShortestPathsSearch LAZY_KSHORTEST = + new LazyKShortestPathsSearch<>(); + private static LinkWeigher defaultLinkWeigher = null; private static GraphPathSearch defaultGraphPathSearch = null; @@ -387,7 +396,88 @@ public class DefaultTopology extends AbstractModel implements Topology { } /** - * /** + * 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 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 getKShortestPaths(DeviceId src, DeviceId dst, + LinkWeigher weigher, + int maxPaths) { + DefaultTopologyVertex srcV = new DefaultTopologyVertex(src); + DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst); + Set 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 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 getKShortestPaths(DeviceId src, DeviceId dst, + LinkWeigher weigher) { + DefaultTopologyVertex srcV = new DefaultTopologyVertex(src); + DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst); + Set 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 48403d54c8..481e7f757b 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.TopologyEdge; 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 @@ public class DefaultTopologyTest { 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 ecf7e25cab..48a8d6526f 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 org.slf4j.Logger; 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,10 +170,37 @@ public class TopologyManager 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 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 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 getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) { 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 ffb5a95022..585ed14d96 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.Map; 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; @@ -225,6 +226,22 @@ public class DistributedTopologyStore return defaultTopology(topology).getPaths(src, dst, weigher); } + @Override + public Set getKShortestPaths(Topology topology, + DeviceId src, DeviceId dst, + LinkWeigher weigher, + int maxPaths) { + return defaultTopology(topology).getKShortestPaths(src, dst, weigher, maxPaths); + } + + @Override + public Stream getKShortestPaths(Topology topology, + DeviceId src, + DeviceId dst, + LinkWeigher weigher) { + return defaultTopology(topology).getKShortestPaths(src, dst, weigher); + } + @Override public Set getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) { return defaultTopology(topology).getDisjointPaths(src, dst);