Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/cli/src/main/java/org/onlab/onos/cli/GreetCommand.java b/cli/src/main/java/org/onlab/onos/cli/GreetCommand.java
deleted file mode 100644
index 71e56f1..0000000
--- a/cli/src/main/java/org/onlab/onos/cli/GreetCommand.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.onlab.onos.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.onlab.onos.GreetService;
-
-/**
- * Simple command example to demonstrate use of Karaf shell extensions; shows
- * use of an optional parameter as well.
- */
-@Command(scope = "onos", name = "greet", description = "Issues a greeting")
-public class GreetCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "name", description = "Name to greet",
-              required = false, multiValued = false)
-    String name = "dude";
-
-    @Override
-    protected Object doExecute() throws Exception {
-        print(getService(GreetService.class).yo(name));
-        return null;
-    }
-}
diff --git a/cli/src/main/java/org/onlab/onos/cli/NameCompleter.java b/cli/src/main/java/org/onlab/onos/cli/NameCompleter.java
deleted file mode 100644
index bcf0707..0000000
--- a/cli/src/main/java/org/onlab/onos/cli/NameCompleter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.onlab.onos.cli;
-
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.completer.StringsCompleter;
-import org.onlab.onos.GreetService;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.SortedSet;
-
-/**
- * Simple example of a command-line parameter completer.
- * For a more open-ended sets a more efficient implementation would be required.
- */
-public class NameCompleter implements Completer {
-    @Override
-    public int complete(String buffer, int cursor, List<String> candidates) {
-        // Delegate string completer
-        StringsCompleter delegate = new StringsCompleter();
-
-        // Fetch our service and feed it's offerings to the string completer
-        GreetService greetService = AbstractShellCommand.get(GreetService.class);
-        Iterator<String> it = greetService.names().iterator();
-        SortedSet<String> strings = delegate.getStrings();
-        while (it.hasNext()) {
-            strings.add(it.next());
-        }
-
-        // Now let the completer do the work for figuring out what to offer.
-        return delegate.complete(buffer, cursor, candidates);
-    }
-
-}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java
new file mode 100644
index 0000000..d4d6d24
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java
@@ -0,0 +1,47 @@
+package org.onlab.onos.cli.net;
+
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.topology.TopologyCluster;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static org.onlab.onos.net.topology.ClusterId.clusterId;
+
+/**
+ * Lists devices of the specified topology cluster in the current topology.
+ */
+@Command(scope = "onos", name = "cluster-devices",
+         description = "Lists devices of the specified topology cluster in the current topology")
+public class ClusterDevicesCommand extends ClustersListCommand {
+
+    @Argument(index = 0, name = "id", description = "Cluster ID",
+              required = false, multiValued = false)
+    String id = null;
+
+    protected static final Comparator<DeviceId> ID_COMPARATOR = new Comparator<DeviceId>() {
+        @Override
+        public int compare(DeviceId id1, DeviceId id2) {
+            return id1.uri().toString().compareTo(id2.uri().toString());
+        }
+    };
+
+    @Override
+    protected Object doExecute() throws Exception {
+        int cid = Integer.parseInt(id);
+        init();
+        TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
+        List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster));
+        Collections.sort(ids, ID_COMPARATOR);
+        for (DeviceId deviceId : ids) {
+            print("%s", deviceId);
+        }
+        return null;
+    }
+
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClusterIdCompleter.java b/cli/src/main/java/org/onlab/onos/cli/net/ClusterIdCompleter.java
new file mode 100644
index 0000000..e39e01b
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClusterIdCompleter.java
@@ -0,0 +1,35 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyCluster;
+import org.onlab.onos.net.topology.TopologyService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Cluster ID completer.
+ */
+public class ClusterIdCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        // Fetch our service and feed it's offerings to the string completer
+        TopologyService service = AbstractShellCommand.get(TopologyService.class);
+        Topology topology = service.currentTopology();
+
+        SortedSet<String> strings = delegate.getStrings();
+        for (TopologyCluster cluster : service.getClusters(topology)) {
+            strings.add(Integer.toString(cluster.id().index()));
+        }
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
new file mode 100644
index 0000000..5285418
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.topology.TopologyCluster;
+
+import static org.onlab.onos.cli.net.LinksListCommand.linkString;
+import static org.onlab.onos.net.topology.ClusterId.clusterId;
+
+/**
+ * Lists links of the specified topology cluster in the current topology.
+ */
+@Command(scope = "onos", name = "cluster-links",
+         description = "Lists links of the specified topology cluster in the current topology")
+public class ClusterLinksCommand extends ClustersListCommand {
+
+    @Argument(index = 0, name = "id", description = "Cluster ID",
+              required = false, multiValued = false)
+    String id = null;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        int cid = Integer.parseInt(id);
+        init();
+        TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
+        for (Link link : service.getClusterLinks(topology, cluster)) {
+            print(linkString(link));
+        }
+        return null;
+    }
+
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java
new file mode 100644
index 0000000..355825b
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java
@@ -0,0 +1,41 @@
+package org.onlab.onos.cli.net;
+
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.net.topology.TopologyCluster;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Lists all clusters in the current topology.
+ */
+@Command(scope = "onos", name = "clusters",
+         description = "Lists all clusters in the current topology")
+public class ClustersListCommand extends TopologyCommand {
+
+    private static final String FMT =
+            "id=%s, devices=%d, links=%d";
+
+    protected static final Comparator<TopologyCluster> ID_COMPARATOR =
+            new Comparator<TopologyCluster>() {
+                @Override
+                public int compare(TopologyCluster c1, TopologyCluster c2) {
+                    return c1.id().index() - c2.id().index();
+                }
+            };
+
+    @Override
+    protected Object doExecute() throws Exception {
+        init();
+        List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology));
+        Collections.sort(clusters, ID_COMPARATOR);
+
+        for (TopologyCluster cluster : clusters) {
+            print(FMT, cluster.id(), cluster.deviceCount(), cluster.linkCount());
+        }
+        return null;
+    }
+
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
index cb6fcb1..6c3952d 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
@@ -27,9 +27,20 @@
         Iterable<Link> links = uri != null ?
                 service.getDeviceLinks(deviceId(uri)) : service.getLinks();
         for (Link link : links) {
-            print(FMT, link.src().deviceId(), link.src().port(),
-                  link.dst().deviceId(), link.dst().port(), link.type());
+            print(linkString(link));
         }
         return null;
     }
+
+    /**
+     * Returns a formated string representing the gien link.
+     *
+     * @param link infrastructure link
+     * @return formated link string
+     */
+    public static String linkString(Link link) {
+        return String.format(FMT, link.src().deviceId(), link.src().port(),
+                             link.dst().deviceId(), link.dst().port(), link.type());
+
+    }
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
new file mode 100644
index 0000000..a390025
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyService;
+
+/**
+ * Lists summary of the current topology.
+ */
+@Command(scope = "onos", name = "topology",
+         description = "Lists summary of the current topology")
+public class TopologyCommand extends AbstractShellCommand {
+
+    private static final String FMT =
+            "time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
+
+    protected TopologyService service;
+    protected Topology topology;
+
+    /**
+     * Initializes the context for all cluster commands.
+     */
+    protected void init() {
+        service = getService(TopologyService.class);
+        topology = service.currentTopology();
+    }
+
+    @Override
+    protected Object doExecute() throws Exception {
+        init();
+        print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
+              topology.clusterCount(), topology.pathCount());
+        return null;
+    }
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 96c494d..65a6c9e 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -32,16 +32,27 @@
         </command>
 
         <command>
-            <action class="org.onlab.onos.cli.GreetCommand"/>
+            <action class="org.onlab.onos.cli.net.TopologyCommand"/>
+        </command>
+        <command>
+            <action class="org.onlab.onos.cli.net.ClustersListCommand"/>
+        </command>
+        <command>
+            <action class="org.onlab.onos.cli.net.ClusterDevicesCommand"/>
             <completers>
-                <ref component-id="nameCompleter"/>
+                <ref component-id="clusterIdCompleter"/>
+            </completers>
+        </command>
+        <command>
+            <action class="org.onlab.onos.cli.net.ClusterLinksCommand"/>
+            <completers>
+                <ref component-id="clusterIdCompleter"/>
             </completers>
         </command>
     </command-bundle>
 
     <bean id="deviceIdCompleter" class="org.onlab.onos.cli.net.DeviceIdCompleter"/>
+    <bean id="clusterIdCompleter" class="org.onlab.onos.cli.net.ClusterIdCompleter"/>
     <bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/>
 
-    <bean id="nameCompleter" class="org.onlab.onos.cli.NameCompleter"/>
-
 </blueprint>
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java b/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
index 1f7783c..e937bf3 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
@@ -16,7 +16,7 @@
     private final Type type;
 
     /**
-     * Creates a link description using the supplied information.
+     * Creates an infrastructure link using the supplied information.
      *
      * @param providerId provider identity
      * @param src        link source
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java b/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java
new file mode 100644
index 0000000..63c9e88
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultPath.java
@@ -0,0 +1,71 @@
+package org.onlab.onos.net;
+
+import com.google.common.collect.ImmutableList;
+import org.onlab.onos.net.provider.ProviderId;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of a network path.
+ */
+public class DefaultPath extends DefaultLink implements Path {
+
+    private final List<Link> links;
+    private final double cost;
+
+    /**
+     * Creates a path from the specified source and destination using the
+     * supplied list of links.
+     *
+     * @param providerId provider identity
+     * @param links      contiguous links that comprise the path
+     * @param cost       unit-less path cost
+     */
+    public DefaultPath(ProviderId providerId, List<Link> links, double cost) {
+        super(providerId, source(links), destination(links), Type.INDIRECT);
+        this.links = ImmutableList.copyOf(links);
+        this.cost = cost;
+    }
+
+    @Override
+    public List<Link> links() {
+        return links;
+    }
+
+    @Override
+    public double cost() {
+        return cost;
+    }
+
+    // Returns the source of the first link.
+    private static ConnectPoint source(List<Link> links) {
+        checkNotNull(links, "List of path links cannot be null");
+        checkArgument(!links.isEmpty(), "List of path links cannot be empty");
+        return links.get(0).src();
+    }
+
+    // Returns the destination of the last link.
+    private static ConnectPoint destination(List<Link> links) {
+        checkNotNull(links, "List of path links cannot be null");
+        checkArgument(!links.isEmpty(), "List of path links cannot be empty");
+        return links.get(links.size() - 1).dst();
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * super.hashCode() + Objects.hash(links);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof DefaultPath) {
+            final DefaultPath other = (DefaultPath) obj;
+            return Objects.equals(this.links, other.links);
+        }
+        return false;
+    }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/Path.java b/core/api/src/main/java/org/onlab/onos/net/Path.java
index 22a363a..acf8170 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Path.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Path.java
@@ -17,4 +17,11 @@
      */
     List<Link> links();
 
+    /**
+     * Returns the path cost as a unit-less value.
+     *
+     * @return unit-less path cost
+     */
+    double cost();
+
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/ClusterId.java b/core/api/src/main/java/org/onlab/onos/net/topology/ClusterId.java
index af8f122..502eee1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/ClusterId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/ClusterId.java
@@ -27,6 +27,15 @@
         return new ClusterId(id);
     }
 
+    /**
+     * Returns the backing integer index.
+     *
+     * @return backing integer index
+     */
+    public int index() {
+        return id;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(id);
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/GraphDescription.java b/core/api/src/main/java/org/onlab/onos/net/topology/GraphDescription.java
new file mode 100644
index 0000000..2b846a0
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/GraphDescription.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.net.topology;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.onos.net.Description;
+
+/**
+ * Describes attribute(s) of a network graph.
+ */
+public interface GraphDescription extends Description {
+
+    /**
+     * Returns the creation timestamp of the graph description. This is
+     * expressed in system nanos to allow proper sequencing.
+     *
+     * @return graph description creation timestamp
+     */
+    long timestamp();
+
+    /**
+     * Returns the set of topology graph vertexes.
+     *
+     * @return set of graph vertexes
+     */
+    ImmutableSet<TopologyVertex> vertexes();
+
+    /**
+     * Returns the set of topology graph edges.
+     *
+     * @return set of graph edges
+     */
+    ImmutableSet<TopologyEdge> edges();
+
+}
+
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/LinkWeight.java b/core/api/src/main/java/org/onlab/onos/net/topology/LinkWeight.java
index 89ef577..5e18dfb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/LinkWeight.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/LinkWeight.java
@@ -6,5 +6,5 @@
  * Entity capable of determining cost or weight of a specified topology
  * graph edge.
  */
-public interface LinkWeight extends EdgeWeight<TopoVertex, TopoEdge> {
+public interface LinkWeight extends EdgeWeight<TopologyVertex, TopologyEdge> {
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java
deleted file mode 100644
index dd0102d..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.onlab.onos.net.topology;
-
-import org.onlab.graph.Graph;
-import org.onlab.graph.GraphPathSearch;
-import org.onlab.onos.net.Description;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Link;
-
-import java.util.Set;
-
-/**
- * Describes attribute(s) of a network topology.
- */
-public interface TopologyDescription extends Description {
-
-    /**
-     * Returns the creation timestamp of the topology description. This is
-     * expressed in system nanos to allow proper sequencing.
-     *
-     * @return topology description creation timestamp
-     */
-    long timestamp();
-
-    /**
-     * Returns the topology graph.
-     *
-     * @return network graph
-     */
-    Graph<TopoVertex, TopoEdge> graph();
-
-    /**
-     * Returns the results of the path search through the network graph. This
-     * is assumed to contain results of seach fro the given device to all
-     * other devices.
-     *
-     * @param srcDeviceId source device identifier
-     * @return path search result for the given source node
-     */
-    GraphPathSearch.Result pathResults(DeviceId srcDeviceId);
-
-    /**
-     * Returns the set of topology SCC clusters.
-     *
-     * @return set of SCC clusters
-     */
-    Set<TopologyCluster> clusters();
-
-    /**
-     * Returns the set of devices contained by the specified topology cluster.
-     *
-     * @return set of devices that belong to the specified cluster
-     */
-    Set<DeviceId> clusterDevices(TopologyCluster cluster);
-
-    /**
-     * Returns the set of infrastructure links contained by the specified cluster.
-     *
-     * @return set of links that form the given cluster
-     */
-    Set<Link> clusterLinks(TopologyCluster cluster);
-
-    /**
-     * Returns the topology SCC cluster which contains the given device.
-     *
-     * @param deviceId device identifier
-     * @return topology cluster that contains the specified device
-     */
-    TopologyCluster clusterFor(DeviceId deviceId);
-
-}
-
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopoEdge.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyEdge.java
similarity index 82%
rename from core/api/src/main/java/org/onlab/onos/net/topology/TopoEdge.java
rename to core/api/src/main/java/org/onlab/onos/net/topology/TopologyEdge.java
index ef94eca..d9cd6f4 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopoEdge.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyEdge.java
@@ -6,7 +6,7 @@
 /**
  * Represents an edge in the topology graph.
  */
-public interface TopoEdge extends Edge<TopoVertex> {
+public interface TopologyEdge extends Edge<TopologyVertex> {
 
     /**
      * Returns the associated infrastructure link.
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyGraph.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyGraph.java
new file mode 100644
index 0000000..70139cd
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyGraph.java
@@ -0,0 +1,10 @@
+package org.onlab.onos.net.topology;
+
+import org.onlab.graph.Graph;
+
+/**
+ * Represents an immutable topology graph.
+ */
+public interface TopologyGraph extends Graph<TopologyVertex, TopologyEdge> {
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProviderService.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProviderService.java
index 0e03767..c81a871 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyProviderService.java
@@ -10,16 +10,13 @@
  */
 public interface TopologyProviderService extends ProviderService<TopologyProvider> {
 
-    // What can be conveyed in a topology that isn't by individual
-    // providers?
-
     /**
      * Signals the core that some aspect of the topology has changed.
      *
-     * @param topoDescription information about topology
-     * @param reasons         events that triggered topology change
+     * @param graphDescription information about the network graph
+     * @param reasons          events that triggered topology change
      */
-    void topologyChanged(TopologyDescription topoDescription,
+    void topologyChanged(GraphDescription graphDescription,
                          List<Event> reasons);
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
index 7c6be33..3469a55 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
@@ -1,8 +1,8 @@
 package org.onlab.onos.net.topology;
 
-import org.onlab.graph.Graph;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
 import org.onlab.onos.net.Path;
 
 import java.util.Set;
@@ -21,12 +21,21 @@
 
     /**
      * Indicates whether the specified topology is the latest or not.
+     *
      * @param topology topology descriptor
      * @return true if the topology is the most recent; false otherwise
      */
     boolean isLatest(Topology topology);
 
     /**
+     * Returns the graph view of the specified topology.
+     *
+     * @param topology topology descriptor
+     * @return topology graph view
+     */
+    TopologyGraph getGraph(Topology topology);
+
+    /**
      * Returns the set of clusters in the specified topology.
      *
      * @param topology topology descriptor
@@ -35,12 +44,31 @@
     Set<TopologyCluster> getClusters(Topology topology);
 
     /**
-     * Returns the graph view of the specified topology.
+     * Returns the cluster with the specified ID.
      *
-     * @param topology topology descriptor
-     * @return topology graph view
+     * @param topology  topology descriptor
+     * @param clusterId cluster identifier
+     * @return topology cluster
      */
-    Graph<TopoVertex, TopoEdge> getGraph(Topology topology);
+    TopologyCluster getCluster(Topology topology, ClusterId clusterId);
+
+    /**
+     * Returns the set of devices that belong to the specified cluster.
+     *
+     * @param topology  topology descriptor
+     * @param cluster topology cluster
+     * @return set of cluster devices
+     */
+    Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster);
+
+    /**
+     * Returns the set of links that form the specified cluster.
+     *
+     * @param topology  topology descriptor
+     * @param cluster topology cluster
+     * @return set of cluster links
+     */
+    Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster);
 
     /**
      * Returns the set of all shortest paths, precomputed in terms of hop-count,
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopoVertex.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyVertex.java
similarity index 86%
rename from core/api/src/main/java/org/onlab/onos/net/topology/TopoVertex.java
rename to core/api/src/main/java/org/onlab/onos/net/topology/TopologyVertex.java
index d5a8b95..96a918f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopoVertex.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyVertex.java
@@ -6,7 +6,7 @@
 /**
  * Represents a vertex in the topology graph.
  */
-public interface TopoVertex extends Vertex {
+public interface TopologyVertex extends Vertex {
 
     /**
      * Returns the associated infrastructure device identification.
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
similarity index 98%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostManager.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
index 6f0b39e..0bb291f1 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.net.trivial.host.impl;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
similarity index 98%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
index e142de7..96dce86 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
@@ -1,4 +1,15 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.net.trivial.host.impl;
+
+import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
+import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
+import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
+import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultHost;
@@ -15,17 +26,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
-
 /**
  * Manages inventory of end-station hosts using trivial in-memory
  * implementation.
@@ -36,6 +36,7 @@
 
     // hosts sorted based on their location
     private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
+
     /**
      * Creates a new host or updates the existing one based on the specified
      * description.
@@ -46,7 +47,7 @@
      * @return appropriate event or null if no change resulted
      */
     HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
-                                 HostDescription hostDescription) {
+            HostDescription hostDescription) {
         Host host = hosts.get(hostId);
         if (host == null) {
             return createHost(providerId, hostId, hostDescription);
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultGraphDescription.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultGraphDescription.java
new file mode 100644
index 0000000..59ef8df
--- /dev/null
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultGraphDescription.java
@@ -0,0 +1,90 @@
+package org.onlab.onos.net.trivial.topology.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.topology.GraphDescription;
+import org.onlab.onos.net.topology.TopologyEdge;
+import org.onlab.onos.net.topology.TopologyVertex;
+
+import java.util.Map;
+
+/**
+ * Default implementation of an immutable topology graph data carrier.
+ */
+class DefaultGraphDescription implements GraphDescription {
+
+    private final long nanos;
+    private final ImmutableSet<TopologyVertex> vertexes;
+    private final ImmutableSet<TopologyEdge> edges;
+
+    private final Map<DeviceId, TopologyVertex> vertexesById = Maps.newHashMap();
+
+
+    /**
+     * Creates a minimal topology graph description to allow core to construct
+     * and process the topology graph.
+     *
+     * @param nanos   time in nanos of when the topology description was created
+     * @param devices collection of infrastructure devices
+     * @param links   collection of infrastructure links
+     */
+    DefaultGraphDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) {
+        this.nanos = nanos;
+        this.vertexes = buildVertexes(devices);
+        this.edges = buildEdges(links);
+        vertexesById.clear();
+    }
+
+    @Override
+    public long timestamp() {
+        return nanos;
+    }
+
+    @Override
+    public ImmutableSet<TopologyVertex> vertexes() {
+        return vertexes;
+    }
+
+    @Override
+    public ImmutableSet<TopologyEdge> edges() {
+        return edges;
+    }
+
+    // Builds a set of topology vertexes from the specified list of devices
+    private ImmutableSet<TopologyVertex> buildVertexes(Iterable<Device> devices) {
+        ImmutableSet.Builder<TopologyVertex> vertexes = ImmutableSet.builder();
+        for (Device device : devices) {
+            TopologyVertex vertex = new DefaultTopologyVertex(device.id());
+            vertexes.add(vertex);
+            vertexesById.put(vertex.deviceId(), vertex);
+        }
+        return vertexes.build();
+    }
+
+    // Builds a set of topology vertexes from the specified list of links
+    private ImmutableSet<TopologyEdge> buildEdges(Iterable<Link> links) {
+        ImmutableSet.Builder<TopologyEdge> edges = ImmutableSet.builder();
+        for (Link link : links) {
+            edges.add(new DefaultTopologyEdge(vertexOf(link.src()),
+                                              vertexOf(link.dst()), link));
+        }
+        return edges.build();
+    }
+
+    // Fetches a vertex corresponding to the given connection point device.
+    private TopologyVertex vertexOf(ConnectPoint connectPoint) {
+        DeviceId id = connectPoint.deviceId();
+        TopologyVertex vertex = vertexesById.get(id);
+        if (vertex == null) {
+            // If vertex does not exist, create one and register it.
+            vertex = new DefaultTopologyVertex(id);
+            vertexesById.put(id, vertex);
+        }
+        return vertex;
+    }
+
+}
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
index c20acd8..ef87867 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
@@ -1,8 +1,38 @@
 package org.onlab.onos.net.trivial.topology.impl;
 
+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.TarjanGraphSearch;
 import org.onlab.onos.net.AbstractModel;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultPath;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
 import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.topology.ClusterId;
+import org.onlab.onos.net.topology.DefaultTopologyCluster;
+import org.onlab.onos.net.topology.GraphDescription;
+import org.onlab.onos.net.topology.LinkWeight;
 import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyCluster;
+import org.onlab.onos.net.topology.TopologyEdge;
+import org.onlab.onos.net.topology.TopologyGraph;
+import org.onlab.onos.net.topology.TopologyVertex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.onos.net.Link.Type.INDIRECT;
 
 /**
  * Default implementation of the topology descriptor. This carries the
@@ -10,30 +40,53 @@
  */
 public class DefaultTopology extends AbstractModel implements Topology {
 
+    private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
+            new DijkstraGraphSearch<>();
+    private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
+            new TarjanGraphSearch<>();
+
+    private static final ProviderId PID = new ProviderId("org.onlab.onos.net");
+
     private final long time;
-    private final int clusterCount;
-    private final int deviceCount;
-    private final int linkCount;
-    private final int pathCount;
+    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 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;
+
 
     /**
      * Creates a topology descriptor attributed to the specified provider.
      *
-     * @param providerId   identity of the provider
-     * @param time         creation time in system nanos
-     * @param clusterCount number of clusters
-     * @param deviceCount  number of devices
-     * @param linkCount    number of links
-     * @param pathCount    number of pre-computed paths
+     * @param providerId  identity of the provider
+     * @param description data describing the new topology
      */
-    DefaultTopology(ProviderId providerId, long time, int clusterCount,
-                    int deviceCount, int linkCount, int pathCount) {
+    DefaultTopology(ProviderId providerId, GraphDescription description) {
         super(providerId);
-        this.time = time;
-        this.clusterCount = clusterCount;
-        this.deviceCount = deviceCount;
-        this.linkCount = linkCount;
-        this.pathCount = pathCount;
+        this.time = description.timestamp();
+
+        // Build the graph
+        this.graph = new DefaultTopologyGraph(description.vertexes(),
+                                              description.edges());
+
+        this.results = searchForShortestPaths();
+        this.paths = buildPaths();
+
+        this.clusterResults = searchForClusters();
+        this.clusters = buildTopologyClusters();
+
+        buildIndexes();
+
+        this.broadcastSets = buildBroadcastSets();
+        this.infrastructurePoints = findInfrastructurePoints();
     }
 
     @Override
@@ -43,22 +96,329 @@
 
     @Override
     public int clusterCount() {
-        return clusterCount;
+        return clusters.size();
     }
 
     @Override
     public int deviceCount() {
-        return deviceCount;
+        return graph.getVertexes().size();
     }
 
     @Override
     public int linkCount() {
-        return linkCount;
+        return graph.getEdges().size();
     }
 
     @Override
     public int pathCount() {
-        return pathCount;
+        return paths.size();
     }
 
+    /**
+     * Returns the backing topology graph.
+     *
+     * @return topology graph
+     */
+    TopologyGraph getGraph() {
+        return graph;
+    }
+
+    /**
+     * Returns the set of topology clusters.
+     *
+     * @return set of clusters
+     */
+    Set<TopologyCluster> getClusters() {
+        return ImmutableSet.copyOf(clusters.values());
+    }
+
+    /**
+     * Returns the specified topology cluster.
+     *
+     * @param clusterId cluster identifier
+     * @return topology cluster
+     */
+    TopologyCluster getCluster(ClusterId clusterId) {
+        return clusters.get(clusterId);
+    }
+
+
+    /**
+     * Returns the set of cluster devices.
+     *
+     * @param cluster topology cluster
+     * @return cluster devices
+     */
+    Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
+        return devicesByCluster.get(cluster);
+    }
+
+    /**
+     * Returns the set of cluster links.
+     *
+     * @param cluster topology cluster
+     * @return cluster links
+     */
+    Set<Link> getClusterLinks(TopologyCluster cluster) {
+        return linksByCluster.get(cluster);
+    }
+
+    /**
+     * Indicates whether the given point is an infrastructure link end-point.
+     *
+     * @param connectPoint connection point
+     * @return true if infrastructure
+     */
+    boolean isInfrastructure(ConnectPoint connectPoint) {
+        return infrastructurePoints.contains(connectPoint);
+    }
+
+    /**
+     * Indicates whether the given point is part of a broadcast tree.
+     *
+     * @param connectPoint connection point
+     * @return true if in broadcast tree
+     */
+    boolean isInBroadcastTree(ConnectPoint connectPoint) {
+        // Any non-infrastructure, i.e. edge points are assumed to be OK
+        if (!isInfrastructure(connectPoint)) {
+            return true;
+        }
+
+        // Find the cluster to which the device belongs.
+        TopologyCluster cluster = clustersByDevice.get(connectPoint.deviceId());
+        if (cluster == null) {
+            throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
+        }
+
+        // If the broadcast tree is null or empty, or if the point explicitly
+        // belongs to the broadcast tree points, return true;
+        Set<ConnectPoint> points = broadcastSets.get(cluster.id());
+        return points == null || points.isEmpty() || points.contains(connectPoint);
+    }
+
+    /**
+     * Returns the set of pre-computed shortest paths between source and
+     * destination devices.
+     *
+     * @param src source device
+     * @param dst destination device
+     * @return set of shortest paths
+     */
+    Set<Path> getPaths(DeviceId src, DeviceId dst) {
+        return paths.get(new PathKey(src, dst));
+    }
+
+    /**
+     * Computes on-demand the set of shortest paths between source and
+     * destination devices.
+     *
+     * @param src source device
+     * @param dst destination device
+     * @return set of shortest paths
+     */
+    Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
+        GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
+                DIJKSTRA.search(graph, new DefaultTopologyVertex(src),
+                                new DefaultTopologyVertex(dst), weight);
+        ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
+        for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
+            builder.add(networkPath(path));
+        }
+        return builder.build();
+    }
+
+
+    // 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<>();
+        for (TopologyEdge edge : path.edges()) {
+            links.add(edge.link());
+        }
+        return new DefaultPath(PID, links, path.cost());
+    }
+
+
+    // Searches for SCC clusters in the network topology graph using Tarjan
+    // algorithm.
+    private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
+        return TARJAN.search(graph, new NoIndirectLinksWeight());
+    }
+
+    // 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());
+
+        // 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();
+
+        // Scan over the lists and create a cluster from the results.
+        for (int i = 0, n = result.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());
+            clusterBuilder.put(cid, cluster);
+        }
+        return clusterBuilder.build();
+    }
+
+    // Finds the vertex whose device id is the lexicographical minimum in the
+    // specified set.
+    private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
+        TopologyVertex minVertex = null;
+        for (TopologyVertex vertex : vertexSet) {
+            if (minVertex == null ||
+                    minVertex.deviceId().toString()
+                            .compareTo(minVertex.deviceId().toString()) < 0) {
+                minVertex = vertex;
+            }
+        }
+        return minVertex;
+    }
+
+    // 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()) {
+            addClusterBroadcastSet(cluster, builder);
+        }
+        return builder.build();
+    }
+
+    // Finds all broadcast points for the cluster. These are those connection
+    // points which lie along the shortest paths between the cluster root and
+    // all other devices within the cluster.
+    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());
+        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) {
+                continue;
+            }
+
+            // Ignore any back-link sets that are empty.
+            Set<TopologyEdge> parents = entry.getValue();
+            if (parents.isEmpty()) {
+                continue;
+            }
+
+            // Use the first back-link source and destinations to add to the
+            // broadcast set.
+            Link link = parents.iterator().next().link();
+            builder.put(cluster.id(), link.src());
+            builder.put(cluster.id(), link.dst());
+        }
+    }
+
+    // Collects and returns an set of all infrastructure link end-points.
+    private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
+        ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+        for (TopologyEdge edge : graph.getEdges()) {
+            builder.add(edge.link().src());
+            builder.add(edge.link().dst());
+        }
+        return builder.build();
+    }
+
+    // Builds cluster-devices, cluster-links and device-cluster indexes.
+    private void 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()) {
+            int i = cluster.id().index();
+
+            // Scan through all the cluster vertexes.
+            for (TopologyVertex vertex : clusterResults.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)) {
+                linksBuilder.put(cluster, edge.link());
+            }
+        }
+
+        // Finalize all indexes.
+        clustersByDevice = clusterBuilder.build();
+        devicesByCluster = devicesBuilder.build();
+        linksByCluster = linksBuilder.build();
+    }
+
+    // Link weight for measuring link cost as hop count with indirect links
+    // being as expensive as traversing the entire graph to assume the worst.
+    private static class HopCountLinkWeight implements LinkWeight {
+        private final int indirectLinkCost;
+
+        HopCountLinkWeight(int indirectLinkCost) {
+            this.indirectLinkCost = indirectLinkCost;
+        }
+
+        @Override
+        public double weight(TopologyEdge edge) {
+            // To force preference to use direct paths first, make indirect
+            // links as expensive as the linear vertex traversal.
+            return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
+        }
+    }
+
+    // Link weight for preventing traversal over indirect links.
+    private static class NoIndirectLinksWeight implements LinkWeight {
+        @Override
+        public double weight(TopologyEdge edge) {
+            return edge.link().type() == INDIRECT ? -1 : 1;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("time", time)
+                .add("clusters", clusterCount())
+                .add("devices", deviceCount())
+                .add("links", linkCount())
+                .add("pathCount", pathCount())
+                .toString();
+    }
 }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopoEdge.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyEdge.java
similarity index 64%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopoEdge.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyEdge.java
index d940754..e3a1a9e 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopoEdge.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyEdge.java
@@ -1,8 +1,8 @@
-package org.onlab.onos.net.trivial.topology.provider.impl;
+package org.onlab.onos.net.trivial.topology.impl;
 
 import org.onlab.onos.net.Link;
-import org.onlab.onos.net.topology.TopoEdge;
-import org.onlab.onos.net.topology.TopoVertex;
+import org.onlab.onos.net.topology.TopologyEdge;
+import org.onlab.onos.net.topology.TopologyVertex;
 
 import java.util.Objects;
 
@@ -11,11 +11,11 @@
 /**
  * Implementation of the topology edge backed by a link.
  */
-class DefaultTopoEdge implements TopoEdge {
+class DefaultTopologyEdge implements TopologyEdge {
 
     private final Link link;
-    private final TopoVertex src;
-    private final TopoVertex dst;
+    private final TopologyVertex src;
+    private final TopologyVertex dst;
 
     /**
      * Creates a new topology edge.
@@ -24,7 +24,7 @@
      * @param dst  destination vertex
      * @param link infrastructure link
      */
-    DefaultTopoEdge(TopoVertex src, TopoVertex dst, Link link) {
+    DefaultTopologyEdge(TopologyVertex src, TopologyVertex dst, Link link) {
         this.src = src;
         this.dst = dst;
         this.link = link;
@@ -36,12 +36,12 @@
     }
 
     @Override
-    public TopoVertex src() {
+    public TopologyVertex src() {
         return src;
     }
 
     @Override
-    public TopoVertex dst() {
+    public TopologyVertex dst() {
         return dst;
     }
 
@@ -52,8 +52,8 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof DefaultTopoEdge) {
-            final DefaultTopoEdge other = (DefaultTopoEdge) obj;
+        if (obj instanceof DefaultTopologyEdge) {
+            final DefaultTopologyEdge other = (DefaultTopologyEdge) obj;
             return Objects.equals(this.link, other.link);
         }
         return false;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyGraph.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyGraph.java
new file mode 100644
index 0000000..22681b7
--- /dev/null
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyGraph.java
@@ -0,0 +1,28 @@
+package org.onlab.onos.net.trivial.topology.impl;
+
+import org.onlab.graph.AdjacencyListsGraph;
+import org.onlab.onos.net.topology.TopologyEdge;
+import org.onlab.onos.net.topology.TopologyGraph;
+import org.onlab.onos.net.topology.TopologyVertex;
+
+import java.util.Set;
+
+/**
+ * Default implementation of an immutable topology graph based on a generic
+ * implementation of adjacency lists graph.
+ */
+public class DefaultTopologyGraph
+        extends AdjacencyListsGraph<TopologyVertex, TopologyEdge>
+        implements TopologyGraph {
+
+    /**
+     * Creates a topology graph comprising of the specified vertexes and edges.
+     *
+     * @param vertexes set of graph vertexes
+     * @param edges    set of graph edges
+     */
+    public DefaultTopologyGraph(Set<TopologyVertex> vertexes, Set<TopologyEdge> edges) {
+        super(vertexes, edges);
+    }
+
+}
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java
similarity index 86%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java
index 777a0e4..6b0e036 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProvider.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.topology.provider.impl;
+package org.onlab.onos.net.trivial.topology.impl;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -16,7 +16,7 @@
 import org.onlab.onos.net.link.LinkService;
 import org.onlab.onos.net.provider.AbstractProvider;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.net.topology.TopologyDescription;
+import org.onlab.onos.net.topology.GraphDescription;
 import org.onlab.onos.net.topology.TopologyProvider;
 import org.onlab.onos.net.topology.TopologyProviderRegistry;
 import org.onlab.onos.net.topology.TopologyProviderService;
@@ -32,10 +32,12 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Simple implementation of a network topology provider/computor.
+ * Default implementation of a network topology provider that feeds off
+ * device and link subsystem events to trigger assembly and computation of
+ * new topology snapshots.
  */
 @Component(immediate = true)
-public class SimpleTopologyProvider extends AbstractProvider
+public class DefaultTopologyProvider extends AbstractProvider
         implements TopologyProvider {
 
     // TODO: make these configurable
@@ -70,13 +72,13 @@
     /**
      * Creates a provider with the supplier identifier.
      */
-    public SimpleTopologyProvider() {
+    public DefaultTopologyProvider() {
         super(new ProviderId("org.onlab.onos.provider.topology"));
     }
 
     @Activate
     public synchronized void activate() {
-        executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-compute-%d"));
+        executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-build-%d"));
         accumulator = new TopologyChangeAccumulator();
 
         providerService = providerRegistry.register(this);
@@ -90,6 +92,8 @@
 
     @Deactivate
     public synchronized void deactivate() {
+        isStarted = false;
+
         deviceService.removeListener(deviceListener);
         linkService.removeListener(linkListener);
         providerRegistry.unregister(this);
@@ -98,7 +102,6 @@
         executor.shutdownNow();
         executor = null;
 
-        isStarted = false;
         log.info("Stopped");
     }
 
@@ -108,18 +111,20 @@
      *
      * @param reasons events which triggered the topology change
      */
-    private void triggerTopologyBuild(List<Event> reasons) {
-        executor.execute(new TopologyBuilderTask(reasons));
+    private synchronized void triggerTopologyBuild(List<Event> reasons) {
+        if (executor != null) {
+            executor.execute(new TopologyBuilderTask(reasons));
+        }
     }
 
     // Builds the topology using the latest device and link information
     // and citing the specified events as reasons for the change.
     private void buildTopology(List<Event> reasons) {
         if (isStarted) {
-            TopologyDescription desc =
-                    new DefaultTopologyDescription(System.nanoTime(),
-                                                   deviceService.getDevices(),
-                                                   linkService.getLinks());
+            GraphDescription desc =
+                    new DefaultGraphDescription(System.nanoTime(),
+                                                deviceService.getDevices(),
+                                                linkService.getLinks());
             providerService.topologyChanged(desc, reasons);
         }
     }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopoVertex.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyVertex.java
similarity index 69%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopoVertex.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyVertex.java
index afcaca6..043e364 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopoVertex.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyVertex.java
@@ -1,14 +1,14 @@
-package org.onlab.onos.net.trivial.topology.provider.impl;
+package org.onlab.onos.net.trivial.topology.impl;
 
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.topology.TopoVertex;
+import org.onlab.onos.net.topology.TopologyVertex;
 
 import java.util.Objects;
 
 /**
  * Implementation of the topology vertex backed by a device id.
  */
-class DefaultTopoVertex implements TopoVertex {
+class DefaultTopologyVertex implements TopologyVertex {
 
     private final DeviceId deviceId;
 
@@ -17,7 +17,7 @@
      *
      * @param deviceId backing infrastructure device identifier
      */
-    DefaultTopoVertex(DeviceId deviceId) {
+    DefaultTopologyVertex(DeviceId deviceId) {
         this.deviceId = deviceId;
     }
 
@@ -33,8 +33,8 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof DefaultTopoVertex) {
-            final DefaultTopoVertex other = (DefaultTopoVertex) obj;
+        if (obj instanceof DefaultTopologyVertex) {
+            final DefaultTopologyVertex other = (DefaultTopologyVertex) obj;
             return Objects.equals(this.deviceId, other.deviceId);
         }
         return false;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/PathKey.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/PathKey.java
new file mode 100644
index 0000000..ea993a4
--- /dev/null
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/PathKey.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.net.trivial.topology.impl;
+
+import org.onlab.onos.net.DeviceId;
+
+import java.util.Objects;
+
+/**
+ * Key for filing pre-computed paths between source and destination devices.
+ */
+class PathKey {
+    private final DeviceId src;
+    private final DeviceId dst;
+
+    /**
+     * Creates a path key from the given source/dest pair.
+     * @param src source device
+     * @param dst destination device
+     */
+    PathKey(DeviceId src, DeviceId dst) {
+        this.src = src;
+        this.dst = dst;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(src, dst);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PathKey) {
+            final PathKey other = (PathKey) obj;
+            return Objects.equals(this.src, other.src) && Objects.equals(this.dst, other.dst);
+        }
+        return false;
+    }
+}
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
index 70f001c..6ad8f4b 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
@@ -6,22 +6,22 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
-import org.onlab.graph.Graph;
 import org.onlab.onos.event.AbstractListenerRegistry;
 import org.onlab.onos.event.Event;
 import org.onlab.onos.event.EventDeliveryService;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
 import org.onlab.onos.net.Path;
 import org.onlab.onos.net.provider.AbstractProviderRegistry;
 import org.onlab.onos.net.provider.AbstractProviderService;
+import org.onlab.onos.net.topology.ClusterId;
+import org.onlab.onos.net.topology.GraphDescription;
 import org.onlab.onos.net.topology.LinkWeight;
-import org.onlab.onos.net.topology.TopoEdge;
-import org.onlab.onos.net.topology.TopoVertex;
 import org.onlab.onos.net.topology.Topology;
 import org.onlab.onos.net.topology.TopologyCluster;
-import org.onlab.onos.net.topology.TopologyDescription;
 import org.onlab.onos.net.topology.TopologyEvent;
+import org.onlab.onos.net.topology.TopologyGraph;
 import org.onlab.onos.net.topology.TopologyListener;
 import org.onlab.onos.net.topology.TopologyProvider;
 import org.onlab.onos.net.topology.TopologyProviderRegistry;
@@ -46,6 +46,8 @@
 
     public static final String TOPOLOGY_NULL = "Topology cannot be null";
     private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+    private static final String CLUSTER_ID_NULL = "Cluster ID cannot be null";
+    private static final String CLUSTER_NULL = "Topology cluster cannot be null";
     public static final String CONNECTION_POINT_NULL = "Connection point cannot be null";
 
     private final Logger log = getLogger(getClass());
@@ -79,19 +81,49 @@
     @Override
     public boolean isLatest(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.isLatest(topology);
+        return store.isLatest(defaultTopology(topology));
+    }
+
+    // Validates the specified topology and returns it as a default
+    private DefaultTopology defaultTopology(Topology topology) {
+        if (topology instanceof DefaultTopology) {
+            return (DefaultTopology) topology;
+        }
+        throw new IllegalArgumentException("Topology class " + topology.getClass() +
+                                                   " not supported");
     }
 
     @Override
     public Set<TopologyCluster> getClusters(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.getClusters(topology);
+        return store.getClusters(defaultTopology(topology));
     }
 
     @Override
-    public Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
+    public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.getGraph(topology);
+        checkNotNull(topology, CLUSTER_ID_NULL);
+        return store.getCluster(defaultTopology(topology), clusterId);
+    }
+
+    @Override
+    public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(topology, CLUSTER_NULL);
+        return store.getClusterDevices(defaultTopology(topology), cluster);
+    }
+
+    @Override
+    public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(topology, CLUSTER_NULL);
+        return store.getClusterLinks(defaultTopology(topology), cluster);
+    }
+
+    @Override
+    public TopologyGraph getGraph(Topology topology) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        return store.getGraph(defaultTopology(topology));
     }
 
     @Override
@@ -99,7 +131,7 @@
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
-        return store.getPaths(topology, src, dst);
+        return store.getPaths(defaultTopology(topology), src, dst);
     }
 
     @Override
@@ -108,21 +140,21 @@
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
         checkNotNull(weight, "Link weight cannot be null");
-        return store.getPaths(topology, src, dst, weight);
+        return store.getPaths(defaultTopology(topology), src, dst, weight);
     }
 
     @Override
     public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(connectPoint, CONNECTION_POINT_NULL);
-        return store.isInfrastructure(topology, connectPoint);
+        return store.isInfrastructure(defaultTopology(topology), connectPoint);
     }
 
     @Override
     public boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(connectPoint, CONNECTION_POINT_NULL);
-        return store.isInBroadcastTree(topology, connectPoint);
+        return store.isInBroadcastTree(defaultTopology(topology), connectPoint);
     }
 
     @Override
@@ -150,15 +182,14 @@
         }
 
         @Override
-        public void topologyChanged(TopologyDescription topoDescription,
+        public void topologyChanged(GraphDescription topoDescription,
                                     List<Event> reasons) {
             checkNotNull(topoDescription, "Topology description cannot be null");
 
-            log.info("Topology changed due to: {}", // to be removed soon
-                     reasons == null ? "initial compute" : reasons);
-            TopologyEvent event = store.updateTopology(topoDescription, reasons);
+            TopologyEvent event = store.updateTopology(provider().id(),
+                                                       topoDescription, reasons);
             if (event != null) {
-                log.info("Topology changed due to: {}",
+                log.info("Topology {} changed due to: {}", event.subject(),
                          reasons == null ? "initial compute" : reasons);
                 eventDispatcher.post(event);
             }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java
index 1b9766d..f432ade 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java
@@ -1,17 +1,18 @@
 package org.onlab.onos.net.trivial.topology.impl;
 
-import org.onlab.graph.Graph;
 import org.onlab.onos.event.Event;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
 import org.onlab.onos.net.Path;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.topology.ClusterId;
+import org.onlab.onos.net.topology.GraphDescription;
 import org.onlab.onos.net.topology.LinkWeight;
-import org.onlab.onos.net.topology.TopoEdge;
-import org.onlab.onos.net.topology.TopoVertex;
 import org.onlab.onos.net.topology.Topology;
 import org.onlab.onos.net.topology.TopologyCluster;
-import org.onlab.onos.net.topology.TopologyDescription;
 import org.onlab.onos.net.topology.TopologyEvent;
+import org.onlab.onos.net.topology.TopologyGraph;
 
 import java.util.List;
 import java.util.Set;
@@ -45,23 +46,56 @@
     }
 
     /**
-     * Returns the set of topology SCC clusters.
-     *
-     * @param topology topology descriptor
-     * @return set of clusters
-     */
-    Set<TopologyCluster> getClusters(Topology topology) {
-        return null;
-    }
-
-    /**
      * Returns the immutable graph view of the current topology.
      *
      * @param topology topology descriptor
      * @return graph view
      */
-    Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
-        return null;
+    TopologyGraph getGraph(DefaultTopology topology) {
+        return topology.getGraph();
+    }
+
+    /**
+     * Returns the set of topology SCC clusters.
+     *
+     * @param topology topology descriptor
+     * @return set of clusters
+     */
+    Set<TopologyCluster> getClusters(DefaultTopology topology) {
+        return topology.getClusters();
+    }
+
+    /**
+     * Returns the cluster of the specified topology.
+     *
+     * @param topology  topology descriptor
+     * @param clusterId cluster identity
+     * @return topology cluster
+     */
+    TopologyCluster getCluster(DefaultTopology topology, ClusterId clusterId) {
+        return topology.getCluster(clusterId);
+    }
+
+    /**
+     * Returns the cluster of the specified topology.
+     *
+     * @param topology  topology descriptor
+     * @param cluster topology cluster
+     * @return set of cluster links
+     */
+    Set<DeviceId> getClusterDevices(DefaultTopology topology, TopologyCluster cluster) {
+        return topology.getClusterDevices(cluster);
+    }
+
+    /**
+     * Returns the cluster of the specified topology.
+     *
+     * @param topology  topology descriptor
+     * @param cluster topology cluster
+     * @return set of cluster links
+     */
+    Set<Link> getClusterLinks(DefaultTopology topology, TopologyCluster cluster) {
+        return topology.getClusterLinks(cluster);
     }
 
     /**
@@ -72,8 +106,8 @@
      * @param dst      destination device
      * @return set of shortest paths
      */
-    Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
-        return null;
+    Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst) {
+        return topology.getPaths(src, dst);
     }
 
     /**
@@ -85,9 +119,9 @@
      * @param weight   link weight function
      * @return set of shortest paths
      */
-    Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+    Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst,
                        LinkWeight weight) {
-        return null;
+        return topology.getPaths(src, dst, weight);
     }
 
     /**
@@ -97,8 +131,8 @@
      * @param connectPoint connection point
      * @return true if infrastructure; false otherwise
      */
-    boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
-        return false;
+    boolean isInfrastructure(DefaultTopology topology, ConnectPoint connectPoint) {
+        return topology.isInfrastructure(connectPoint);
     }
 
     /**
@@ -108,20 +142,36 @@
      * @param connectPoint connection point
      * @return true if in broadcast tree; false otherwise
      */
-    boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) {
-        return false;
+    boolean isInBroadcastTree(DefaultTopology topology, ConnectPoint connectPoint) {
+        return topology.isInBroadcastTree(connectPoint);
     }
 
     /**
      * Generates a new topology snapshot from the specified description.
      *
-     * @param topoDescription topology description
-     * @param reasons         list of events that triggered the update
+     * @param providerId       provider identification
+     * @param graphDescription topology graph description
+     * @param reasons          list of events that triggered the update
      * @return topology update event or null if the description is old
      */
-    TopologyEvent updateTopology(TopologyDescription topoDescription,
+    TopologyEvent updateTopology(ProviderId providerId,
+                                 GraphDescription graphDescription,
                                  List<Event> reasons) {
-        return null;
+        // First off, make sure that what we're given is indeed newer than
+        // what we already have.
+        if (current != null && graphDescription.timestamp() < current.time()) {
+            return null;
+        }
+
+        // Have the default topology construct self from the description data.
+        DefaultTopology newTopology =
+                new DefaultTopology(providerId, graphDescription);
+
+        // Promote the new topology to current and return a ready-to-send event.
+        synchronized (this) {
+            current = newTopology;
+            return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
+        }
     }
 
 }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java
deleted file mode 100644
index 408e6af..0000000
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java
+++ /dev/null
@@ -1,247 +0,0 @@
-package org.onlab.onos.net.trivial.topology.provider.impl;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.onlab.graph.AdjacencyListsGraph;
-import org.onlab.graph.DijkstraGraphSearch;
-import org.onlab.graph.Graph;
-import org.onlab.graph.GraphPathSearch;
-import org.onlab.graph.TarjanGraphSearch;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Link;
-import org.onlab.onos.net.topology.ClusterId;
-import org.onlab.onos.net.topology.DefaultTopologyCluster;
-import org.onlab.onos.net.topology.LinkWeight;
-import org.onlab.onos.net.topology.TopoEdge;
-import org.onlab.onos.net.topology.TopoVertex;
-import org.onlab.onos.net.topology.TopologyCluster;
-import org.onlab.onos.net.topology.TopologyDescription;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-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.onos.net.Link.Type.INDIRECT;
-
-/**
- * Default implementation of an immutable topology data carrier.
- */
-class DefaultTopologyDescription implements TopologyDescription {
-
-    private static final GraphPathSearch<TopoVertex, TopoEdge> DIJKSTRA =
-            new DijkstraGraphSearch<>();
-    private static final TarjanGraphSearch<TopoVertex, TopoEdge> TARJAN =
-            new TarjanGraphSearch<>();
-
-    private final long nanos;
-    private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap();
-    private final Graph<TopoVertex, TopoEdge> graph;
-    private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results;
-    private final Map<ClusterId, TopologyCluster> clusters;
-
-    // Secondary look-up indexes
-    private ImmutableSetMultimap<ClusterId, DeviceId> devicesByCluster;
-    private ImmutableSetMultimap<ClusterId, Link> linksByCluster;
-    private Map<DeviceId, TopologyCluster> clustersByDevice = Maps.newHashMap();
-
-    /**
-     * Creates a topology description to carry topology vitals to the core.
-     *
-     * @param nanos   time in nanos of when the topology description was created
-     * @param devices collection of infrastructure devices
-     * @param links   collection of infrastructure links
-     */
-    DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) {
-        this.nanos = nanos;
-        this.graph = buildGraph(devices, links);
-        this.results = computeDefaultPaths();
-        this.clusters = computeClusters();
-    }
-
-    @Override
-    public long timestamp() {
-        return nanos;
-    }
-
-    @Override
-    public Graph<TopoVertex, TopoEdge> graph() {
-        return graph;
-    }
-
-    @Override
-    public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) {
-        return results.get(srcDeviceId);
-    }
-
-    @Override
-    public Set<TopologyCluster> clusters() {
-        return ImmutableSet.copyOf(clusters.values());
-    }
-
-    @Override
-    public Set<DeviceId> clusterDevices(TopologyCluster cluster) {
-        return devicesByCluster.get(cluster.id());
-    }
-
-    @Override
-    public Set<Link> clusterLinks(TopologyCluster cluster) {
-        return linksByCluster.get(cluster.id());
-    }
-
-    @Override
-    public TopologyCluster clusterFor(DeviceId deviceId) {
-        return clustersByDevice.get(deviceId);
-    }
-
-
-    // Link weight for measuring link cost as hop count with indirect links
-    // being as expensive as traversing the entire graph to assume the worst.
-    private static class HopCountLinkWeight implements LinkWeight {
-        private final int indirectLinkCost;
-
-        HopCountLinkWeight(int indirectLinkCost) {
-            this.indirectLinkCost = indirectLinkCost;
-        }
-
-        @Override
-        public double weight(TopoEdge edge) {
-            // To force preference to use direct paths first, make indirect
-            // links as expensive as the linear vertex traversal.
-            return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
-        }
-    }
-
-    // Link weight for preventing traversal over indirect links.
-    private static class NoIndirectLinksWeight implements LinkWeight {
-        @Override
-        public double weight(TopoEdge edge) {
-            return edge.link().type() == INDIRECT ? -1 : 1;
-        }
-    }
-
-    // Constructs the topology graph using the supplied devices and links.
-    private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices,
-                                                   Iterable<Link> links) {
-        return new AdjacencyListsGraph<>(buildVertexes(devices),
-                                         buildEdges(links));
-    }
-
-    // Builds a set of topology vertexes from the specified list of devices
-    private Set<TopoVertex> buildVertexes(Iterable<Device> devices) {
-        Set<TopoVertex> vertexes = Sets.newHashSet();
-        for (Device device : devices) {
-            TopoVertex vertex = new DefaultTopoVertex(device.id());
-            vertexes.add(vertex);
-            vertexesById.put(vertex.deviceId(), vertex);
-        }
-        return vertexes;
-    }
-
-    // Builds a set of topology vertexes from the specified list of links
-    private Set<TopoEdge> buildEdges(Iterable<Link> links) {
-        Set<TopoEdge> edges = Sets.newHashSet();
-        for (Link link : links) {
-            edges.add(new DefaultTopoEdge(vertexOf(link.src()),
-                                          vertexOf(link.dst()), link));
-        }
-        return edges;
-    }
-
-    // Computes the default shortest paths for all source/dest pairs using
-    // the multi-path Dijkstra and hop-count as path cost.
-    private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() {
-        LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
-        Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap();
-
-        // Search graph paths for each source to all destinations.
-        for (TopoVertex src : vertexesById.values()) {
-            results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
-        }
-        return results;
-    }
-
-    // Computes topology SCC clusters using Tarjan algorithm.
-    private Map<ClusterId, TopologyCluster> computeClusters() {
-        Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap();
-        SCCResult<TopoVertex, TopoEdge> result = TARJAN.search(graph, new NoIndirectLinksWeight());
-
-        // Extract both vertexes and edges from the results; the lists form
-        // pairs along the same index.
-        List<Set<TopoVertex>> clusterVertexes = result.clusterVertexes();
-        List<Set<TopoEdge>> clusterEdges = result.clusterEdges();
-
-        Builder<ClusterId, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
-        Builder<ClusterId, Link> linksBuilder = ImmutableSetMultimap.builder();
-
-        // Scan over the lists and create a cluster from the results.
-        for (int i = 0, n = result.clusterCount(); i < n; i++) {
-            Set<TopoVertex> vertexSet = clusterVertexes.get(i);
-            Set<TopoEdge> edgeSet = clusterEdges.get(i);
-
-            DefaultTopologyCluster cluster =
-                    new DefaultTopologyCluster(ClusterId.clusterId(i),
-                                               vertexSet.size(), edgeSet.size(),
-                                               findRoot(vertexSet).deviceId());
-            findClusterDevices(vertexSet, cluster, devicesBuilder);
-            findClusterLinks(edgeSet, cluster, linksBuilder);
-        }
-        return clusters;
-    }
-
-    // Scans through the set of cluster vertexes and puts their devices in a
-    // multi-map associated with the cluster. It also binds the devices to
-    // the cluster.
-    private void findClusterDevices(Set<TopoVertex> vertexSet,
-                                    DefaultTopologyCluster cluster,
-                                    Builder<ClusterId, DeviceId> builder) {
-        for (TopoVertex vertex : vertexSet) {
-            DeviceId deviceId = vertex.deviceId();
-            builder.put(cluster.id(), deviceId);
-            clustersByDevice.put(deviceId, cluster);
-        }
-    }
-
-    // Scans through the set of cluster edges and puts their links in a
-    // multi-map associated with the cluster.
-    private void findClusterLinks(Set<TopoEdge> edgeSet,
-                                  DefaultTopologyCluster cluster,
-                                  Builder<ClusterId, Link> builder) {
-        for (TopoEdge edge : edgeSet) {
-            builder.put(cluster.id(), edge.link());
-        }
-    }
-
-    // Finds the vertex whose device id is the lexicographical minimum in the
-    // specified set.
-    private TopoVertex findRoot(Set<TopoVertex> vertexSet) {
-        TopoVertex minVertex = null;
-        for (TopoVertex vertex : vertexSet) {
-            if (minVertex == null ||
-                    minVertex.deviceId().toString()
-                            .compareTo(minVertex.deviceId().toString()) < 0) {
-                minVertex = vertex;
-            }
-        }
-        return minVertex;
-    }
-
-    // Fetches a vertex corresponding to the given connection point device.
-    private TopoVertex vertexOf(ConnectPoint connectPoint) {
-        DeviceId id = connectPoint.deviceId();
-        TopoVertex vertex = vertexesById.get(id);
-        if (vertex == null) {
-            // If vertex does not exist, create one and register it.
-            vertex = new DefaultTopoVertex(id);
-            vertexesById.put(id, vertex);
-        }
-        return vertex;
-    }
-
-}
diff --git a/core/trivial/src/main/javadoc/org/onlab/onos/net/trivial/topology/provider/impl/package.html b/core/trivial/src/main/javadoc/org/onlab/onos/net/trivial/topology/provider/impl/package.html
deleted file mode 100644
index ee67aa8..0000000
--- a/core/trivial/src/main/javadoc/org/onlab/onos/net/trivial/topology/provider/impl/package.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<body>
-Built-in protocol-agnostic topology builder &amp; provider.
-</body>
\ No newline at end of file
diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java b/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java
index 65434be..60580db 100644
--- a/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java
+++ b/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java
@@ -7,6 +7,7 @@
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
 
@@ -83,12 +84,18 @@
 
     @Override
     public Integer inPort() {
-        return pktin.getInPort().getPortNumber();
+        try {
+            return pktin.getInPort().getPortNumber();
+        } catch (UnsupportedOperationException e) {
+            return pktin.getMatch().get(MatchField.IN_PORT).getPortNumber();
+        }
     }
 
     @Override
     public byte[] unparsed() {
+
         return pktin.getData().clone();
+
     }
 
     private OFActionOutput buildOutput(Integer port) {
diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java
index b0a6305..bade605 100644
--- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java
+++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java
@@ -250,9 +250,12 @@
                 }
                 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
                 if (sw == null) {
-                    log.error("Transition to equal called on sw {}, but switch "
-                            + "was not found in controller-cache", dpid);
-                    return;
+                    sw = getSwitch(dpid);
+                    if (sw == null) {
+                        log.error("Transition to equal called on sw {}, but switch "
+                                + "was not found in controller-cache", dpid);
+                        return;
+                    }
                 }
                 log.info("Transitioned switch {} to EQUAL", dpid);
                 activeEqualSwitches.put(dpid, sw);
diff --git a/of/ctl/src/main/java/org/onlab/onos/of/drivers/impl/DriverManager.java b/of/ctl/src/main/java/org/onlab/onos/of/drivers/impl/DriverManager.java
index 2c72c17..1744a76 100644
--- a/of/ctl/src/main/java/org/onlab/onos/of/drivers/impl/DriverManager.java
+++ b/of/ctl/src/main/java/org/onlab/onos/of/drivers/impl/DriverManager.java
@@ -2,10 +2,15 @@
 
 
 
+import java.util.Collections;
+import java.util.List;
+
 import org.onlab.onos.of.controller.Dpid;
+import org.onlab.onos.of.controller.driver.AbstractOpenFlowSwitch;
 import org.onlab.onos.of.controller.driver.OpenFlowSwitchDriver;
 import org.onlab.onos.of.controller.driver.OpenFlowSwitchDriverFactory;
 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,8 +56,36 @@
         }
 
         log.warn("DriverManager could not identify switch desc: {}. "
-                + "Assigning OFSwitchImplBase", desc);
-        return null;
+                + "Assigning AbstractOpenFlowSwich", desc);
+        return new AbstractOpenFlowSwitch(dpid) {
+
+            @Override
+            public void write(List<OFMessage> msgs) {
+                channel.write(msgs);
+            }
+
+            @Override
+            public void write(OFMessage msg) {
+                channel.write(Collections.singletonList(msg));
+
+            }
+
+            @Override
+            public Boolean supportNxRole() {
+                return false;
+            }
+
+            @Override
+            public void startDriverHandshake() {}
+
+            @Override
+            public void processDriverHandshakeMessage(OFMessage m) {}
+
+            @Override
+            public boolean isDriverHandshakeComplete() {
+                return true;
+            }
+        };
     }
 
     /**
diff --git a/pom.xml b/pom.xml
index 1002901..3e872e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -320,6 +320,7 @@
                 <artifactId>maven-javadoc-plugin</artifactId>
                 <version>2.9.1</version>
                 <configuration>
+                    <show>package</show>
                     <docfilessubdirs>true</docfilessubdirs>
                     <doctitle>ONOS Java API</doctitle>
                     <groups>
diff --git a/providers/of/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/of/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
index 61b1c18..78316da 100644
--- a/providers/of/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/of/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -60,7 +60,7 @@
      * Creates an OpenFlow device provider.
      */
     public OpenFlowDeviceProvider() {
-        super(new ProviderId("org.onlab.onos.provider.of.device"));
+        super(new ProviderId("org.onlab.onos.provider.openflow"));
     }
 
     @Activate
diff --git a/providers/of/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java b/providers/of/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
index 2421481..e05bed9 100644
--- a/providers/of/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
+++ b/providers/of/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
@@ -37,7 +37,7 @@
      * Creates an OpenFlow host provider.
      */
     public OpenFlowHostProvider() {
-        super(new ProviderId("org.onlab.onos.provider.of.host"));
+        super(new ProviderId("org.onlab.onos.provider.openflow"));
     }
 
     @Activate
diff --git a/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java b/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
index 218145d..02239ab 100644
--- a/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
+++ b/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
@@ -55,7 +55,7 @@
      * Creates an OpenFlow link provider.
      */
     public OpenFlowLinkProvider() {
-        super(new ProviderId("org.onlab.onos.provider.of.link"));
+        super(new ProviderId("org.onlab.onos.provider.openflow"));
     }
 
     @Activate