ONOS-745 Refactoring topology to compute only broadcast tree and not pre-compute paths.
ONOS-744 Refactoring graph search to allow requesting max number of paths.

Change-Id: I28467246b92df32ebb3155c45774ecc051fdd3dd
diff --git a/cli/src/main/java/org/onosproject/cli/SummaryCommand.java b/cli/src/main/java/org/onosproject/cli/SummaryCommand.java
index 3d874df..8d17079 100644
--- a/cli/src/main/java/org/onosproject/cli/SummaryCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/SummaryCommand.java
@@ -69,20 +69,18 @@
                     .put("links", topology.linkCount())
                     .put("hosts", get(HostService.class).getHostCount())
                     .put("SCC(s)", topology.clusterCount())
-                    .put("paths", topology.pathCount())
                     .put("flows", get(FlowRuleService.class).getFlowRuleCount())
                     .put("intents", get(IntentService.class).getIntentCount()));
         } else {
             print("node=%s, version=%s",
                   get(ClusterService.class).getLocalNode().ip(),
                   get(CoreService.class).version().toString());
-            print("nodes=%d, devices=%d, links=%d, hosts=%d, SCC(s)=%s, paths=%d, flows=%d, intents=%d",
+            print("nodes=%d, devices=%d, links=%d, hosts=%d, SCC(s)=%s, flows=%d, intents=%d",
                   activeNodes(get(ClusterService.class).getNodes()),
                   get(DeviceService.class).getDeviceCount(),
                   get(LinkService.class).getLinkCount(),
                   get(HostService.class).getHostCount(),
                   topologyService.getClusters(topology).size(),
-                  topology.pathCount(),
                   get(FlowRuleService.class).getFlowRuleCount(),
                   get(IntentService.class).getIntentCount());
         }
diff --git a/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java b/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java
index 0160e3c..337a1f7 100644
--- a/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java
@@ -31,7 +31,7 @@
 public class TopologyCommand extends AbstractShellCommand {
 
     private static final String FMT =
-            "time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
+            "time=%s, devices=%d, links=%d, clusters=%d";
 
     @Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation",
             required = false, multiValued = false)
@@ -59,11 +59,10 @@
                     .put("time", topology.time())
                     .put("deviceCount", topology.deviceCount())
                     .put("linkCount", topology.linkCount())
-                    .put("clusterCount", topology.clusterCount())
-                    .put("pathCount", topology.pathCount()));
+                    .put("clusterCount", topology.clusterCount()));
         } else {
             print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
-                  topology.clusterCount(), topology.pathCount());
+                  topology.clusterCount());
         }
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/topology/DefaultTopologyCluster.java b/core/api/src/main/java/org/onosproject/net/topology/DefaultTopologyCluster.java
index 9eadec1..aaf170f 100644
--- a/core/api/src/main/java/org/onosproject/net/topology/DefaultTopologyCluster.java
+++ b/core/api/src/main/java/org/onosproject/net/topology/DefaultTopologyCluster.java
@@ -15,8 +15,6 @@
  */
 package org.onosproject.net.topology;
 
-import org.onosproject.net.DeviceId;
-
 import java.util.Objects;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
@@ -29,7 +27,7 @@
     private final ClusterId id;
     private final int deviceCount;
     private final int linkCount;
-    private final DeviceId root;
+    private final TopologyVertex root;
 
     /**
      * Creates a new topology cluster descriptor with the specified attributes.
@@ -40,7 +38,7 @@
      * @param root        cluster root node
      */
     public DefaultTopologyCluster(ClusterId id, int deviceCount, int linkCount,
-                                  DeviceId root) {
+                                  TopologyVertex root) {
         this.id = id;
         this.deviceCount = deviceCount;
         this.linkCount = linkCount;
@@ -63,7 +61,7 @@
     }
 
     @Override
-    public DeviceId root() {
+    public TopologyVertex root() {
         return root;
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/topology/Topology.java b/core/api/src/main/java/org/onosproject/net/topology/Topology.java
index d64f0b6..df8d63c 100644
--- a/core/api/src/main/java/org/onosproject/net/topology/Topology.java
+++ b/core/api/src/main/java/org/onosproject/net/topology/Topology.java
@@ -61,13 +61,4 @@
      */
     int linkCount();
 
-    /**
-     * Returns the number of infrastructure paths computed between devices
-     * in the topology. This means the number of all the shortest paths
-     * (hop-count) between all device pairs.
-     *
-     * @return number of paths
-     */
-    int pathCount();
-
 }
diff --git a/core/api/src/main/java/org/onosproject/net/topology/TopologyCluster.java b/core/api/src/main/java/org/onosproject/net/topology/TopologyCluster.java
index fe93c91..fa19afc 100644
--- a/core/api/src/main/java/org/onosproject/net/topology/TopologyCluster.java
+++ b/core/api/src/main/java/org/onosproject/net/topology/TopologyCluster.java
@@ -15,8 +15,6 @@
  */
 package org.onosproject.net.topology;
 
-import org.onosproject.net.DeviceId;
-
 /**
  * Representation of an SCC (strongly-connected component) in a network topology.
  */
@@ -44,10 +42,10 @@
     int linkCount();
 
     /**
-     * Returns the device identifier of the cluster root device.
+     * Returns the cluster root vertex.
      *
-     * @return cluster root device identifier
+     * @return cluster root vertex
      */
-    DeviceId root();
+    TopologyVertex root();
 
 }
diff --git a/core/api/src/test/java/org/onosproject/net/topology/DefaultTopologyClusterTest.java b/core/api/src/test/java/org/onosproject/net/topology/DefaultTopologyClusterTest.java
index 199b773..08b2555 100644
--- a/core/api/src/test/java/org/onosproject/net/topology/DefaultTopologyClusterTest.java
+++ b/core/api/src/test/java/org/onosproject/net/topology/DefaultTopologyClusterTest.java
@@ -43,11 +43,12 @@
         assertEquals("incorrect id", clusterId(6), cluster.id());
         assertEquals("incorrect id", 5, cluster.deviceCount());
         assertEquals("incorrect id", 4, cluster.linkCount());
-        assertEquals("incorrect id", deviceId("of:111"), cluster.root());
+        assertEquals("incorrect id", deviceId("of:111"), cluster.root().deviceId());
 
     }
 
     private TopologyCluster cluster(int id, int dc, int lc, String root) {
-        return new DefaultTopologyCluster(clusterId(id), dc, lc, deviceId(root));
+        return new DefaultTopologyCluster(clusterId(id), dc, lc,
+                                          new DefaultTopologyVertex(deviceId(root)));
     }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java b/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java
index 6eb6c70..302355c 100644
--- a/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java
@@ -31,7 +31,6 @@
 import org.onosproject.net.topology.LinkWeight;
 import org.onosproject.net.topology.Topology;
 import org.onosproject.net.topology.TopologyCluster;
-import org.onosproject.net.topology.TopologyEdge;
 import org.onosproject.net.topology.TopologyEvent;
 import org.onosproject.net.topology.TopologyGraph;
 import org.onosproject.net.topology.TopologyListener;
@@ -127,7 +126,6 @@
         assertEquals("wrong cluster count", 2, topology.clusterCount());
         assertEquals("wrong device count", 6, topology.deviceCount());
         assertEquals("wrong link count", 10, topology.linkCount());
-        assertEquals("wrong path count", 18, topology.pathCount());
 
         assertEquals("wrong cluster count", 2, service.getClusters(topology).size());
 
@@ -148,12 +146,6 @@
         assertFalse("should not be infrastructure point",
                     service.isInfrastructure(topology, new ConnectPoint(did("a"), portNumber(3))));
 
-        // One of these cannot be a broadcast point... or we have a loop...
-//        assertFalse("should not be broadcast point",
-//                    service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(1))) &&
-//                            service.isBroadcastPoint(topology, new ConnectPoint(did("b"), portNumber(1))) &&
-//                            service.isBroadcastPoint(topology, new ConnectPoint(did("c"), portNumber(1))) &&
-//                            service.isBroadcastPoint(topology, new ConnectPoint(did("d"), portNumber(1))));
         assertTrue("should be broadcast point",
                    service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(3))));
     }
@@ -182,12 +174,7 @@
     public void onDemandPath() {
         submitTopologyGraph();
         Topology topology = service.currentTopology();
-        LinkWeight weight = new LinkWeight() {
-            @Override
-            public double weight(TopologyEdge edge) {
-                return 3.3;
-            }
-        };
+        LinkWeight weight = edge -> 3.3;
 
         Set<Path> paths = service.getPaths(topology, did("a"), did("c"), weight);
         assertEquals("wrong path count", 2, paths.size());
diff --git a/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DefaultTopology.java b/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DefaultTopology.java
index 318942c..8c15e45 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DefaultTopology.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DefaultTopology.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
-
 import org.onlab.graph.DijkstraGraphSearch;
 import org.onlab.graph.GraphPathSearch;
 import org.onlab.graph.GraphPathSearch.Result;
@@ -51,11 +50,13 @@
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.collect.ImmutableSetMultimap.Builder;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
 import static org.onosproject.net.Link.State.ACTIVE;
 import static org.onosproject.net.Link.State.INACTIVE;
 import static org.onosproject.net.Link.Type.INDIRECT;
 
+// FIXME: Move to onos-core-common when ready
 /**
  * Default implementation of the topology descriptor. This carries the
  * backing topology data.
@@ -71,10 +72,8 @@
     private final long computeCost;
     private final TopologyGraph graph;
 
+    private final LinkWeight weight;
     private final Supplier<SCCResult<TopologyVertex, TopologyEdge>> clusterResults;
-    private final Supplier<ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>>> results;
-    private final Supplier<ImmutableSetMultimap<PathKey, Path>> paths;
-
     private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
     private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
     private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
@@ -95,15 +94,12 @@
         this.graph = new DefaultTopologyGraph(description.vertexes(),
                                               description.edges());
 
-
-        this.results = Suppliers.memoize(() -> searchForShortestPaths());
-        this.paths = Suppliers.memoize(() -> buildPaths());
-
         this.clusterResults = Suppliers.memoize(() -> searchForClusters());
         this.clusters = Suppliers.memoize(() -> buildTopologyClusters());
 
         this.clusterIndexes = Suppliers.memoize(() -> buildIndexes());
 
+        this.weight = new HopCountLinkWeight(graph.getVertexes().size());
         this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
         this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
         this.computeCost = Math.max(0, System.nanoTime() - time);
@@ -134,11 +130,6 @@
         return graph.getEdges().size();
     }
 
-    @Override
-    public int pathCount() {
-        return paths.get().size();
-    }
-
     private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
         return clusterIndexes.get().clustersByDevice;
     }
@@ -262,7 +253,7 @@
      * @return set of shortest paths
      */
     Set<Path> getPaths(DeviceId src, DeviceId dst) {
-        return paths.get().get(new PathKey(src, dst));
+        return getPaths(src, dst, null);
     }
 
     /**
@@ -284,7 +275,7 @@
         }
 
         GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
-                DIJKSTRA.search(graph, srcV, dstV, weight);
+                DIJKSTRA.search(graph, srcV, dstV, weight, ALL_PATHS);
         ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
         for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
             builder.add(networkPath(path));
@@ -293,31 +284,6 @@
     }
 
 
-    // Searches the graph for all shortest paths and returns the search results.
-    private ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> searchForShortestPaths() {
-        ImmutableMap.Builder<DeviceId, Result<TopologyVertex, TopologyEdge>> builder = ImmutableMap.builder();
-
-        // Search graph paths for each source to all destinations.
-        LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
-        for (TopologyVertex src : graph.getVertexes()) {
-            builder.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
-        }
-        return builder.build();
-    }
-
-    // Builds network paths from the graph path search results
-    private ImmutableSetMultimap<PathKey, Path> buildPaths() {
-        Builder<PathKey, Path> builder = ImmutableSetMultimap.builder();
-        for (DeviceId deviceId : results.get().keySet()) {
-            Result<TopologyVertex, TopologyEdge> result = results.get().get(deviceId);
-            for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
-                builder.put(new PathKey(path.src().deviceId(), path.dst().deviceId()),
-                            networkPath(path));
-            }
-        }
-        return builder.build();
-    }
-
     // Converts graph path to a network path with the same cost.
     private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
         List<Link> links = new ArrayList<>();
@@ -337,23 +303,21 @@
     // Builds the topology clusters and returns the id-cluster bindings.
     private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
         ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
-        SCCResult<TopologyVertex, TopologyEdge> result =
-                TARJAN.search(graph, new NoIndirectLinksWeight());
-
+        SCCResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
         // Extract both vertexes and edges from the results; the lists form
         // pairs along the same index.
-        List<Set<TopologyVertex>> clusterVertexes = result.clusterVertexes();
-        List<Set<TopologyEdge>> clusterEdges = result.clusterEdges();
+        List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
+        List<Set<TopologyEdge>> clusterEdges = results.clusterEdges();
 
         // Scan over the lists and create a cluster from the results.
-        for (int i = 0, n = result.clusterCount(); i < n; i++) {
+        for (int i = 0, n = results.clusterCount(); i < n; i++) {
             Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
             Set<TopologyEdge> edgeSet = clusterEdges.get(i);
 
             ClusterId cid = ClusterId.clusterId(i);
             DefaultTopologyCluster cluster =
                     new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
-                                               findRoot(vertexSet).deviceId());
+                                               findRoot(vertexSet));
             clusterBuilder.put(cid, cluster);
         }
         return clusterBuilder.build();
@@ -388,7 +352,8 @@
     private void addClusterBroadcastSet(TopologyCluster cluster,
                                         Builder<ClusterId, ConnectPoint> builder) {
         // Use the graph root search results to build the broadcast set.
-        Result<TopologyVertex, TopologyEdge> result = results.get().get(cluster.root());
+        Result<TopologyVertex, TopologyEdge> result =
+                DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
         for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
             TopologyVertex vertex = entry.getKey();
 
@@ -498,7 +463,6 @@
                 .add("clusters", clusterCount())
                 .add("devices", deviceCount())
                 .add("links", linkCount())
-                .add("pathCount", pathCount())
                 .toString();
     }
 }
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/DefaultTopology.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/DefaultTopology.java
index f010de4..8dea38b 100644
--- a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/DefaultTopology.java
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/DefaultTopology.java
@@ -15,12 +15,16 @@
  */
 package org.onosproject.store.trivial.impl;
 
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import org.onlab.graph.DijkstraGraphSearch;
 import org.onlab.graph.GraphPathSearch;
+import org.onlab.graph.GraphPathSearch.Result;
 import org.onlab.graph.TarjanGraphSearch;
+import org.onlab.graph.TarjanGraphSearch.SCCResult;
 import org.onosproject.net.AbstractModel;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultPath;
@@ -46,13 +50,13 @@
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.collect.ImmutableSetMultimap.Builder;
-import static org.onlab.graph.GraphPathSearch.Result;
-import static org.onlab.graph.TarjanGraphSearch.SCCResult;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
 import static org.onosproject.net.Link.State.ACTIVE;
 import static org.onosproject.net.Link.State.INACTIVE;
 import static org.onosproject.net.Link.Type.INDIRECT;
 
+// FIXME: Move to onos-core-common when ready
 /**
  * Default implementation of the topology descriptor. This carries the
  * backing topology data.
@@ -68,18 +72,13 @@
     private final long computeCost;
     private final TopologyGraph graph;
 
-    private final SCCResult<TopologyVertex, TopologyEdge> clusterResults;
-    private final ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> results;
-    private final ImmutableSetMultimap<PathKey, Path> paths;
+    private final LinkWeight weight;
+    private final Supplier<SCCResult<TopologyVertex, TopologyEdge>> clusterResults;
+    private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
+    private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
+    private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
 
-    private final ImmutableMap<ClusterId, TopologyCluster> clusters;
-    private final ImmutableSet<ConnectPoint> infrastructurePoints;
-    private final ImmutableSetMultimap<ClusterId, ConnectPoint> broadcastSets;
-
-    private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
-    private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
-    private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
-
+    private final Supplier<ClusterIndexes> clusterIndexes;
 
     /**
      * Creates a topology descriptor attributed to the specified provider.
@@ -95,16 +94,14 @@
         this.graph = new DefaultTopologyGraph(description.vertexes(),
                                               description.edges());
 
-        this.results = searchForShortestPaths();
-        this.paths = buildPaths();
+        this.clusterResults = Suppliers.memoize(() -> searchForClusters());
+        this.clusters = Suppliers.memoize(() -> buildTopologyClusters());
 
-        this.clusterResults = searchForClusters();
-        this.clusters = buildTopologyClusters();
+        this.clusterIndexes = Suppliers.memoize(() -> buildIndexes());
 
-        buildIndexes();
-
-        this.broadcastSets = buildBroadcastSets();
-        this.infrastructurePoints = findInfrastructurePoints();
+        this.weight = new HopCountLinkWeight(graph.getVertexes().size());
+        this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
+        this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
         this.computeCost = Math.max(0, System.nanoTime() - time);
     }
 
@@ -120,7 +117,7 @@
 
     @Override
     public int clusterCount() {
-        return clusters.size();
+        return clusters.get().size();
     }
 
     @Override
@@ -133,9 +130,16 @@
         return graph.getEdges().size();
     }
 
-    @Override
-    public int pathCount() {
-        return paths.size();
+    private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
+        return clusterIndexes.get().clustersByDevice;
+    }
+
+    private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
+        return clusterIndexes.get().devicesByCluster;
+    }
+
+    private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
+        return clusterIndexes.get().linksByCluster;
     }
 
     /**
@@ -153,7 +157,7 @@
      * @return set of clusters
      */
     Set<TopologyCluster> getClusters() {
-        return ImmutableSet.copyOf(clusters.values());
+        return ImmutableSet.copyOf(clusters.get().values());
     }
 
     /**
@@ -163,7 +167,7 @@
      * @return topology cluster
      */
     TopologyCluster getCluster(ClusterId clusterId) {
-        return clusters.get(clusterId);
+        return clusters.get().get(clusterId);
     }
 
     /**
@@ -173,7 +177,7 @@
      * @return topology cluster
      */
     TopologyCluster getCluster(DeviceId deviceId) {
-        return clustersByDevice.get(deviceId);
+        return clustersByDevice().get(deviceId);
     }
 
     /**
@@ -183,7 +187,7 @@
      * @return cluster devices
      */
     Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
-        return devicesByCluster.get(cluster);
+        return devicesByCluster().get(cluster);
     }
 
     /**
@@ -193,7 +197,7 @@
      * @return cluster links
      */
     Set<Link> getClusterLinks(TopologyCluster cluster) {
-        return linksByCluster.get(cluster);
+        return linksByCluster().get(cluster);
     }
 
     /**
@@ -203,7 +207,7 @@
      * @return true if infrastructure
      */
     boolean isInfrastructure(ConnectPoint connectPoint) {
-        return infrastructurePoints.contains(connectPoint);
+        return infrastructurePoints.get().contains(connectPoint);
     }
 
     /**
@@ -219,14 +223,14 @@
         }
 
         // Find the cluster to which the device belongs.
-        TopologyCluster cluster = clustersByDevice.get(connectPoint.deviceId());
+        TopologyCluster cluster = clustersByDevice().get(connectPoint.deviceId());
         if (cluster == null) {
             throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
         }
 
         // If the broadcast set is null or empty, or if the point explicitly
         // belongs to it, return true;
-        Set<ConnectPoint> points = broadcastSets.get(cluster.id());
+        Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
         return points == null || points.isEmpty() || points.contains(connectPoint);
     }
 
@@ -237,7 +241,7 @@
      * @return size of the cluster broadcast set
      */
     int broadcastSetSize(ClusterId clusterId) {
-        return broadcastSets.get(clusterId).size();
+        return broadcastSets.get().get(clusterId).size();
     }
 
     /**
@@ -249,7 +253,7 @@
      * @return set of shortest paths
      */
     Set<Path> getPaths(DeviceId src, DeviceId dst) {
-        return paths.get(new PathKey(src, dst));
+        return getPaths(src, dst, null);
     }
 
     /**
@@ -258,7 +262,7 @@
      *
      * @param src    source device
      * @param dst    destination device
-     * @param weight edge weight function
+     * @param weight link weight function
      * @return set of shortest paths
      */
     Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
@@ -271,7 +275,7 @@
         }
 
         GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
-                DIJKSTRA.search(graph, srcV, dstV, weight);
+                DIJKSTRA.search(graph, srcV, dstV, weight, ALL_PATHS);
         ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
         for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
             builder.add(networkPath(path));
@@ -280,31 +284,6 @@
     }
 
 
-    // Searches the graph for all shortest paths and returns the search results.
-    private ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> searchForShortestPaths() {
-        ImmutableMap.Builder<DeviceId, Result<TopologyVertex, TopologyEdge>> builder = ImmutableMap.builder();
-
-        // Search graph paths for each source to all destinations.
-        LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
-        for (TopologyVertex src : graph.getVertexes()) {
-            builder.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
-        }
-        return builder.build();
-    }
-
-    // Builds network paths from the graph path search results
-    private ImmutableSetMultimap<PathKey, Path> buildPaths() {
-        Builder<PathKey, Path> builder = ImmutableSetMultimap.builder();
-        for (DeviceId deviceId : results.keySet()) {
-            Result<TopologyVertex, TopologyEdge> result = results.get(deviceId);
-            for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
-                builder.put(new PathKey(path.src().deviceId(), path.dst().deviceId()),
-                            networkPath(path));
-            }
-        }
-        return builder.build();
-    }
-
     // Converts graph path to a network path with the same cost.
     private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
         List<Link> links = new ArrayList<>();
@@ -324,23 +303,21 @@
     // Builds the topology clusters and returns the id-cluster bindings.
     private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
         ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
-        SCCResult<TopologyVertex, TopologyEdge> result =
-                TARJAN.search(graph, new NoIndirectLinksWeight());
-
+        SCCResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
         // Extract both vertexes and edges from the results; the lists form
         // pairs along the same index.
-        List<Set<TopologyVertex>> clusterVertexes = result.clusterVertexes();
-        List<Set<TopologyEdge>> clusterEdges = result.clusterEdges();
+        List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
+        List<Set<TopologyEdge>> clusterEdges = results.clusterEdges();
 
         // Scan over the lists and create a cluster from the results.
-        for (int i = 0, n = result.clusterCount(); i < n; i++) {
+        for (int i = 0, n = results.clusterCount(); i < n; i++) {
             Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
             Set<TopologyEdge> edgeSet = clusterEdges.get(i);
 
             ClusterId cid = ClusterId.clusterId(i);
             DefaultTopologyCluster cluster =
                     new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
-                                               findRoot(vertexSet).deviceId());
+                                               findRoot(vertexSet));
             clusterBuilder.put(cid, cluster);
         }
         return clusterBuilder.build();
@@ -363,7 +340,7 @@
     // Processes a map of broadcast sets for each cluster.
     private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
         Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
-        for (TopologyCluster cluster : clusters.values()) {
+        for (TopologyCluster cluster : clusters.get().values()) {
             addClusterBroadcastSet(cluster, builder);
         }
         return builder.build();
@@ -375,12 +352,13 @@
     private void addClusterBroadcastSet(TopologyCluster cluster,
                                         Builder<ClusterId, ConnectPoint> builder) {
         // Use the graph root search results to build the broadcast set.
-        Result<TopologyVertex, TopologyEdge> result = results.get(cluster.root());
+        Result<TopologyVertex, TopologyEdge> result =
+                DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
         for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
             TopologyVertex vertex = entry.getKey();
 
             // Ignore any parents that lead outside the cluster.
-            if (clustersByDevice.get(vertex.deviceId()) != cluster) {
+            if (clustersByDevice().get(vertex.deviceId()) != cluster) {
                 continue;
             }
 
@@ -409,32 +387,32 @@
     }
 
     // Builds cluster-devices, cluster-links and device-cluster indexes.
-    private void buildIndexes() {
+    private ClusterIndexes buildIndexes() {
         // Prepare the index builders
         ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
         ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
         ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
 
         // Now scan through all the clusters
-        for (TopologyCluster cluster : clusters.values()) {
+        for (TopologyCluster cluster : clusters.get().values()) {
             int i = cluster.id().index();
 
             // Scan through all the cluster vertexes.
-            for (TopologyVertex vertex : clusterResults.clusterVertexes().get(i)) {
+            for (TopologyVertex vertex : clusterResults.get().clusterVertexes().get(i)) {
                 devicesBuilder.put(cluster, vertex.deviceId());
                 clusterBuilder.put(vertex.deviceId(), cluster);
             }
 
             // Scan through all the cluster edges.
-            for (TopologyEdge edge : clusterResults.clusterEdges().get(i)) {
+            for (TopologyEdge edge : clusterResults.get().clusterEdges().get(i)) {
                 linksBuilder.put(cluster, edge.link());
             }
         }
 
         // Finalize all indexes.
-        clustersByDevice = clusterBuilder.build();
-        devicesByCluster = devicesBuilder.build();
-        linksByCluster = linksBuilder.build();
+        return new ClusterIndexes(clusterBuilder.build(),
+                                  devicesBuilder.build(),
+                                  linksBuilder.build());
     }
 
     // Link weight for measuring link cost as hop count with indirect links
@@ -463,6 +441,20 @@
         }
     }
 
+    static final class ClusterIndexes {
+        final ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
+        final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
+        final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
+
+        public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
+                              ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
+                              ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
+            this.clustersByDevice = clustersByDevice;
+            this.devicesByCluster = devicesByCluster;
+            this.linksByCluster = linksByCluster;
+        }
+    }
+
     @Override
     public String toString() {
         return toStringHelper(this)
@@ -471,7 +463,6 @@
                 .add("clusters", clusterCount())
                 .add("devices", deviceCount())
                 .add("links", linkCount())
-                .add("pathCount", pathCount())
                 .toString();
     }
 }
diff --git a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/DefaultTopologyTest.java b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/DefaultTopologyTest.java
index 60e55bb..88aa92d 100644
--- a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/DefaultTopologyTest.java
+++ b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/DefaultTopologyTest.java
@@ -17,6 +17,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.ChassisId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.DefaultLink;
@@ -31,8 +32,6 @@
 import org.onosproject.net.topology.GraphDescription;
 import org.onosproject.net.topology.LinkWeight;
 import org.onosproject.net.topology.TopologyCluster;
-import org.onosproject.net.topology.TopologyEdge;
-import org.onlab.packet.ChassisId;
 
 import java.util.Set;
 
@@ -57,13 +56,9 @@
     public static final PortNumber P1 = portNumber(1);
     public static final PortNumber P2 = portNumber(2);
 
-    public static final LinkWeight WEIGHT = new LinkWeight() {
-        @Override
-        public double weight(TopologyEdge edge) {
-            return edge.src().deviceId().equals(D4) ||
-                    edge.dst().deviceId().equals(D4) ? 2.0 : 1.0;
-        }
-    };
+    public static final LinkWeight WEIGHT = edge ->
+            edge.src().deviceId().equals(D4) || edge.dst().deviceId().equals(D4)
+                    ? 2.0 : 1.0;
 
     private DefaultTopology dt;
 
diff --git a/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
index 6541ad3..9554be1 100644
--- a/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
@@ -65,17 +65,31 @@
         protected final Set<Path<V, E>> paths = new HashSet<>();
         protected final Map<V, Double> costs = new HashMap<>();
         protected final Map<V, Set<E>> parents = new HashMap<>();
+        protected final int maxPaths;
 
         /**
-         * Creates the result of path search.
+         * Creates the result of a single-path search.
          *
          * @param src path source
          * @param dst optional path destination
          */
         public DefaultResult(V src, V dst) {
+            this(src, dst, 1);
+        }
+
+        /**
+         * Creates the result of path search.
+         *
+         * @param src      path source
+         * @param dst      optional path destination
+         * @param maxPaths optional limit of number of paths;
+         *                 {@link GraphPathSearch#ALL_PATHS} if no limit
+         */
+        public DefaultResult(V src, V dst, int maxPaths) {
             checkNotNull(src, "Source cannot be null");
             this.src = src;
             this.dst = dst;
+            this.maxPaths = maxPaths;
         }
 
         @Override
@@ -126,7 +140,8 @@
 
         /**
          * Updates the cost of the vertex using its existing cost plus the
-         * cost to traverse the specified edge.
+         * cost to traverse the specified edge. If the search is in single
+         * path mode, only one path will be accrued.
          *
          * @param vertex  vertex to update
          * @param edge    edge through which vertex is reached
@@ -147,7 +162,9 @@
                 if (replace) {
                     edges.clear();
                 }
-                edges.add(edge);
+                if (maxPaths == ALL_PATHS || edges.size() < maxPaths) {
+                    edges.add(edge);
+                }
             }
         }
 
@@ -203,7 +220,7 @@
             for (V v : destinations) {
                 // Ignore the source, if it is among the destinations.
                 if (!v.equals(src)) {
-                    buildAllPaths(this, src, v);
+                    buildAllPaths(this, src, v, maxPaths);
                 }
             }
         }
@@ -215,18 +232,21 @@
      * graph search result by applying breadth-first search through the parent
      * edges and vertex costs.
      *
-     * @param result graph search result
-     * @param src    source vertex
-     * @param dst    destination vertex
+     * @param result   graph search result
+     * @param src      source vertex
+     * @param dst      destination vertex
+     * @param maxPaths limit on the number of paths built;
+     *                 {@link GraphPathSearch#ALL_PATHS} if no limit
      */
-    private void buildAllPaths(DefaultResult result, V src, V dst) {
+    private void buildAllPaths(DefaultResult result, V src, V dst, int maxPaths) {
         DefaultMutablePath<V, E> basePath = new DefaultMutablePath<>();
         basePath.setCost(result.cost(dst));
 
         Set<DefaultMutablePath<V, E>> pendingPaths = new HashSet<>();
         pendingPaths.add(basePath);
 
-        while (!pendingPaths.isEmpty()) {
+        while (!pendingPaths.isEmpty() &&
+                (maxPaths == ALL_PATHS || result.paths.size() < maxPaths)) {
             Set<DefaultMutablePath<V, E>> frontier = new HashSet<>();
 
             for (DefaultMutablePath<V, E> path : pendingPaths) {
diff --git a/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java b/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
index caf8019..39e39e8 100644
--- a/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
@@ -24,11 +24,11 @@
 
     @Override
     public Result<V, E> search(Graph<V, E> graph, V src, V dst,
-                               EdgeWeight<V, E> weight) {
+                               EdgeWeight<V, E> weight, int maxPaths) {
         checkArguments(graph, src, dst);
 
         // Prepare the graph search result.
-        DefaultResult result = new DefaultResult(src, dst);
+        DefaultResult result = new DefaultResult(src, dst, maxPaths);
 
         // The source vertex has cost 0, of course.
         result.updateVertex(src, null, 0.0, true);
diff --git a/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java b/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
index f5c60db..588b0fe 100644
--- a/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
@@ -26,11 +26,11 @@
 
     @Override
     public Result<V, E> search(Graph<V, E> graph, V src, V dst,
-                               EdgeWeight<V, E> weight) {
+                               EdgeWeight<V, E> weight, int maxPaths) {
         checkArguments(graph, src, dst);
 
         // Prepare the graph result.
-        DefaultResult result = new DefaultResult(src, dst);
+        DefaultResult result = new DefaultResult(src, dst, maxPaths);
 
         // Setup the starting frontier with the source as the sole vertex.
         Set<V> frontier = new HashSet<>();
diff --git a/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java b/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
index e61a9c1..f839fff 100644
--- a/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
@@ -36,11 +36,11 @@
 
     @Override
     public SpanningTreeResult search(Graph<V, E> graph, V src, V dst,
-                                     EdgeWeight<V, E> weight) {
+                                     EdgeWeight<V, E> weight, int maxPaths) {
         checkArguments(graph, src, dst);
 
         // Prepare the search result.
-        SpanningTreeResult result = new SpanningTreeResult(src, dst);
+        SpanningTreeResult result = new SpanningTreeResult(src, dst, maxPaths);
 
         // The source vertex has cost 0, of course.
         result.updateVertex(src, null, 0.0, true);
@@ -141,11 +141,12 @@
         /**
          * Creates a new spanning tree result.
          *
-         * @param src search source
-         * @param dst optional search destination
+         * @param src      search source
+         * @param dst      optional search destination
+         * @param maxPaths limit on the number of paths
          */
-        public SpanningTreeResult(V src, V dst) {
-            super(src, dst);
+        public SpanningTreeResult(V src, V dst, int maxPaths) {
+            super(src, dst, maxPaths);
         }
 
         /**
diff --git a/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java b/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
index eada84c..53d211a 100644
--- a/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
@@ -28,12 +28,12 @@
 
     @Override
     public Result<V, E> search(Graph<V, E> graph, V src, V dst,
-                               EdgeWeight<V, E> weight) {
+                               EdgeWeight<V, E> weight, int maxPaths) {
         checkArguments(graph, src, dst);
 
         // Use the default result to remember cumulative costs and parent
         // edges to each each respective vertex.
-        DefaultResult result = new DefaultResult(src, dst);
+        DefaultResult result = new DefaultResult(src, dst, maxPaths);
 
         // Cost to reach the source vertex is 0 of course.
         result.updateVertex(src, null, 0.0, false);
diff --git a/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
index b838421..f869a02 100644
--- a/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
@@ -26,6 +26,8 @@
  */
 public interface GraphPathSearch<V extends Vertex, E extends Edge<V>> {
 
+    public static int ALL_PATHS = -1;
+
     /**
      * Abstraction of a path search result.
      */
@@ -68,16 +70,18 @@
     }
 
     /**
-     * Searches the specified graph.
+     * Searches the specified graph for paths between vertices.
      *
-     * @param graph  graph to be searched
-     * @param src    optional source vertex
-     * @param dst    optional destination vertex; if null paths to all vertex
-     *               destinations will be searched
-     * @param weight optional edge-weight; if null cost of each edge will be
-     *               assumed to be 1.0
+     * @param graph    graph to be searched
+     * @param src      optional source vertex
+     * @param dst      optional destination vertex; if null paths to all vertex
+     *                 destinations will be searched
+     * @param weight   optional edge-weight; if null cost of each edge will be
+     *                 assumed to be 1.0
+     * @param maxPaths limit on number of paths; {@link GraphPathSearch#ALL_PATHS} if no limit
      * @return search results
      */
-    Result<V, E> search(Graph<V, E> graph, V src, V dst, EdgeWeight<V, E> weight);
+    Result<V, E> search(Graph<V, E> graph, V src, V dst,
+                        EdgeWeight<V, E> weight, int maxPaths);
 
 }
diff --git a/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
index 86f29ef..db8402d 100644
--- a/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
@@ -23,6 +23,8 @@
 //import java.util.PriorityQueue;
 import java.util.Set;
 
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
+
 /**
  * K-shortest-path graph search algorithm capable of finding not just one,
  * but K shortest paths with ascending order between the source and destinations.
@@ -137,7 +139,7 @@
     private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) {
         // Determine the shortest path from the source to the destination by using the Dijkstra algorithm.
         DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch();
-        Set<Path> paths =  dijkstraAlg.search(graph, src, dst, weight).paths();
+        Set<Path> paths =  dijkstraAlg.search(graph, src, dst, weight, ALL_PATHS).paths();
         Iterator<Path> itr = paths.iterator();
         if (!itr.hasNext()) {
             return null;
diff --git a/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java
index cf35f66..91204fc 100644
--- a/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java
@@ -36,19 +36,19 @@
     public void noSuchSourceArgument() {
         graphSearch().search(new AdjacencyListsGraph<>(of(B, C),
                                                        of(new TestEdge(B, C, 1))),
-                             A, H, weight);
+                             A, H, weight, 1);
     }
 
     @Test(expected = NullPointerException.class)
     public void nullGraphArgument() {
-        graphSearch().search(null, A, H, weight);
+        graphSearch().search(null, A, H, weight, 1);
     }
 
     @Test(expected = NullPointerException.class)
     public void nullSourceArgument() {
         graphSearch().search(new AdjacencyListsGraph<>(of(B, C),
                                                        of(new TestEdge(B, C, 1))),
-                             null, H, weight);
+                             null, H, weight, 1);
     }
 
     @Test
diff --git a/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java
index 4e9f5e6..9380054 100644
--- a/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java
@@ -21,6 +21,7 @@
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 
 /**
  * Test of the Bellman-Ford algorithm.
@@ -56,7 +57,7 @@
         graph = new AdjacencyListsGraph<>(vertexes, edges);
 
         GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths();
+        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight, ALL_PATHS).paths();
         assertEquals("incorrect paths count", 1, paths.size());
 
         Path p = paths.iterator().next();
@@ -65,10 +66,10 @@
         assertEquals("incorrect path length", 5, p.edges().size());
         assertEquals("incorrect path cost", 5.0, p.cost(), 0.1);
 
-        paths = search.search(graph, A, G, weight).paths();
+        paths = search.search(graph, A, G, weight, ALL_PATHS).paths();
         assertEquals("incorrect paths count", 0, paths.size());
 
-        paths = search.search(graph, A, null, weight).paths();
+        paths = search.search(graph, A, null, weight, ALL_PATHS).paths();
         printPaths(paths);
         assertEquals("incorrect paths count", 6, paths.size());
     }
diff --git a/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java
index 96a77e6..76c0cc2 100644
--- a/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java
@@ -20,6 +20,7 @@
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 
 /**
  * Test of the BFS and similar path search algorithms.
@@ -47,7 +48,8 @@
         graph = new AdjacencyListsGraph<>(vertexes(), edges());
 
         GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths();
+        Set<Path<TestVertex, TestEdge>> paths =
+                search.search(graph, A, H, weight, ALL_PATHS).paths();
         assertEquals("incorrect paths count", 1, paths.size());
 
         Path p = paths.iterator().next();
@@ -56,7 +58,7 @@
         assertEquals("incorrect path length", pathLength, p.edges().size());
         assertEquals("incorrect path cost", pathCost, p.cost(), 0.1);
 
-        paths = search.search(graph, A, null, weight).paths();
+        paths = search.search(graph, A, null, weight, ALL_PATHS).paths();
         printPaths(paths);
         assertEquals("incorrect paths count", pathCount, paths.size());
     }
@@ -68,7 +70,7 @@
                                  EdgeWeight<TestVertex, TestEdge> weight,
                                  int pathCount, double pathCost) {
         GraphPathSearch.Result<TestVertex, TestEdge> result =
-                search.search(graph, src, dst, weight);
+                search.search(graph, src, dst, weight, ALL_PATHS);
         Set<Path<TestVertex, TestEdge>> paths = result.paths();
         printPaths(paths);
         assertEquals("incorrect paths count", pathCount, paths.size());
@@ -78,4 +80,21 @@
         }
     }
 
+    // Executes the single-path search and validates its results.
+    protected void executeSinglePathSearch(GraphPathSearch<TestVertex, TestEdge> search,
+                                           Graph<TestVertex, TestEdge> graph,
+                                           TestVertex src, TestVertex dst,
+                                           EdgeWeight<TestVertex, TestEdge> weight,
+                                           int pathCount, double pathCost) {
+        GraphPathSearch.Result<TestVertex, TestEdge> result =
+                search.search(graph, src, dst, weight, 1);
+        Set<Path<TestVertex, TestEdge>> paths = result.paths();
+        printPaths(paths);
+        assertEquals("incorrect paths count", Math.min(pathCount, 1), paths.size());
+        if (pathCount > 0) {
+            Path<TestVertex, TestEdge> path = paths.iterator().next();
+            assertEquals("incorrect path cost", pathCost, path.cost(), 0.1);
+        }
+    }
+
 }
diff --git a/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java
index d225377..5348bf6 100644
--- a/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.onlab.graph.DepthFirstSearch.EdgeType;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 
 /**
  * Test of the DFS algorithm.
@@ -52,7 +53,7 @@
         DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
 
         DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
-                search.search(graph, A, H, weight);
+                search.search(graph, A, H, weight, 1);
         Set<Path<TestVertex, TestEdge>> paths = result.paths();
         assertEquals("incorrect path count", 1, paths.size());
 
@@ -77,7 +78,7 @@
 
         // Perform narrow path search to a specific destination.
         DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
-                search.search(graph, A, null, weight);
+                search.search(graph, A, null, weight, ALL_PATHS);
         assertEquals("incorrect paths count", 7, result.paths().size());
 
         int[] types = new int[]{0, 0, 0, 0};
diff --git a/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java
index e11ccb2..d906e65 100644
--- a/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java
@@ -55,16 +55,16 @@
                                              new TestEdge(C, D, 1),
                                              new TestEdge(D, C, 1)));
         GraphPathSearch<TestVertex, TestEdge> gs = graphSearch();
-        Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight).paths();
+        Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight, 1).paths();
         printPaths(paths);
         assertEquals("incorrect paths count", 1, paths.size());
         assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
 
-        paths = gs.search(graph, A, D, weight).paths();
+        paths = gs.search(graph, A, D, weight, 1).paths();
         printPaths(paths);
         assertEquals("incorrect paths count", 0, paths.size());
 
-        paths = gs.search(graph, A, null, weight).paths();
+        paths = gs.search(graph, A, null, weight, 1).paths();
         printPaths(paths);
         assertEquals("incorrect paths count", 1, paths.size());
         assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
@@ -78,6 +78,7 @@
                                              new TestEdge(B, D, 1),
                                              new TestEdge(C, D, 1)));
         executeSearch(graphSearch(), graph, A, D, weight, 2, 2.0);
+        executeSinglePathSearch(graphSearch(), graph, A, D, weight, 1, 2.0);
     }
 
     @Test
@@ -93,6 +94,7 @@
                                              new TestEdge(F, G, 1),
                                              new TestEdge(A, G, 4)));
         executeSearch(graphSearch(), graph, A, G, weight, 5, 4.0);
+        executeSinglePathSearch(graphSearch(), graph, A, G, weight, 1, 4.0);
     }
 
     @Test
@@ -106,6 +108,7 @@
                                              new TestEdge(F, G, 1), new TestEdge(F, H, 1),
                                              new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
         executeSearch(graphSearch(), graph, A, E, weight, 3, 3.0);
+        executeSinglePathSearch(graphSearch(), graph, A, E, weight, 1, 3.0);
     }
 
     @Test
@@ -123,6 +126,7 @@
                                              new TestEdge(G, A, -5),
                                              new TestEdge(A, G, 4)));
         executeSearch(graphSearch(), graph, A, G, weight, 3, 4.0);
+        executeSinglePathSearch(graphSearch(), graph, A, G, weight, 1, 4.0);
     }
 
     @Test
diff --git a/web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java b/web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java
index f0ab09f..599881c 100644
--- a/web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java
+++ b/web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java
@@ -454,7 +454,6 @@
                              new Prop("Links", format(topology.linkCount())),
                              new Prop("Hosts", format(hostService.getHostCount())),
                              new Prop("Topology SCCs", format(topology.clusterCount())),
-                             new Prop("Paths", format(topology.pathCount())),
                              new Separator(),
                              new Prop("Intents", format(intentService.getIntentCount())),
                              new Prop("Flows", format(flowService.getFlowRuleCount())),