Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
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 fb44199..65a6c9e 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -30,9 +30,29 @@
                 <ref component-id="deviceIdCompleter"/>
             </completers>
         </command>
+
+        <command>
+            <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="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"/>
 
 </blueprint>
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 d8470e7..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
@@ -2,6 +2,7 @@
 
 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;
@@ -27,6 +28,14 @@
     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
      */
-    TopologyGraph 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/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 86b88b3..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
@@ -133,6 +133,17 @@
     }
 
     /**
+     * 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
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 24bb256..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
@@ -11,9 +11,11 @@
 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.Topology;
@@ -44,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());
@@ -96,6 +100,27 @@
     }
 
     @Override
+    public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        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));
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 b167dbc..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
@@ -3,8 +3,10 @@
 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.Topology;
@@ -44,6 +46,16 @@
     }
 
     /**
+     * Returns the immutable graph view of the current topology.
+     *
+     * @param topology topology descriptor
+     * @return graph view
+     */
+    TopologyGraph getGraph(DefaultTopology topology) {
+        return topology.getGraph();
+    }
+
+    /**
      * Returns the set of topology SCC clusters.
      *
      * @param topology topology descriptor
@@ -54,13 +66,36 @@
     }
 
     /**
-     * Returns the immutable graph view of the current topology.
+     * Returns the cluster of the specified topology.
      *
-     * @param topology topology descriptor
-     * @return graph view
+     * @param topology  topology descriptor
+     * @param clusterId cluster identity
+     * @return topology cluster
      */
-    TopologyGraph getGraph(DefaultTopology topology) {
-        return topology.getGraph();
+    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);
     }
 
     /**