Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
new file mode 100644
index 0000000..8db0d04
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
@@ -0,0 +1,130 @@
+package org.onlab.onos.net.device;
+
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.provider.ProviderId;
+
+import java.util.List;
+
+/**
+ * Manages inventory of infrastructure devices. It may do so using whatever
+ * means are appropriate.
+ */
+public interface DeviceStore {
+
+    /**
+     * Returns the number of devices known to the system.
+     *
+     * @return number of devices
+     */
+    int getDeviceCount();
+
+    /**
+     * Returns an iterable collection of all devices known to the system.
+     *
+     * @return device collection
+     */
+    Iterable<Device> getDevices();
+
+    /**
+     * Returns the device with the specified identifier.
+     *
+     * @param deviceId device identifier
+     * @return device
+     */
+    Device getDevice(DeviceId deviceId);
+
+    /**
+     * Creates a new infrastructure device, or updates an existing one using
+     * the supplied device description.
+     *
+     * @param providerId        provider identifier
+     * @param deviceId          device identifier
+     * @param deviceDescription device description
+     * @return ready to send event describing what occurred; null if no change
+     */
+    DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
+                                     DeviceDescription deviceDescription);
+
+    /**
+     * Removes the specified infrastructure device.
+     *
+     * @param deviceId device identifier
+     * @return ready to send event describing what occurred; null if no change
+     */
+    DeviceEvent markOffline(DeviceId deviceId);
+
+    /**
+     * Updates the ports of the specified infrastructure device using the given
+     * list of port descriptions. The list is assumed to be comprehensive.
+     *
+     * @param deviceId         device identifier
+     * @param portDescriptions list of port descriptions
+     * @return ready to send events describing what occurred; empty list if no change
+     */
+    List<DeviceEvent> updatePorts(DeviceId deviceId,
+                                  List<PortDescription> portDescriptions);
+
+    /**
+     * Updates the port status of the specified infrastructure device using the
+     * given port description.
+     *
+     * @param deviceId        device identifier
+     * @param portDescription port description
+     * @return ready to send event describing what occurred; null if no change
+     */
+    DeviceEvent updatePortStatus(DeviceId deviceId,
+                                 PortDescription portDescription);
+
+    /**
+     * Returns the list of ports that belong to the specified device.
+     *
+     * @param deviceId device identifier
+     * @return list of device ports
+     */
+    List<Port> getPorts(DeviceId deviceId);
+
+    /**
+     * Returns the specified device port.
+     *
+     * @param deviceId   device identifier
+     * @param portNumber port number
+     * @return device port
+     */
+    Port getPort(DeviceId deviceId, PortNumber portNumber);
+
+    /**
+     * Indicates whether the specified device is available/online.
+     *
+     * @param deviceId device identifier
+     * @return true if device is available
+     */
+    boolean isAvailable(DeviceId deviceId);
+
+    /**
+     * Returns the mastership role determined for this device.
+     *
+     * @param deviceId device identifier
+     * @return mastership role
+     */
+    MastershipRole getRole(DeviceId deviceId);
+
+    /**
+     * Administratively sets the role of the specified device.
+     *
+     * @param deviceId device identifier
+     * @param role     mastership role to apply
+     * @return mastership role change event or null if no change
+     */
+    DeviceEvent setRole(DeviceId deviceId, MastershipRole role);
+
+    /**
+     * Administratively removes the specified device from the store.
+     *
+     * @param deviceId device to be removed
+     */
+    DeviceEvent removeDevice(DeviceId deviceId);
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
index f5b31f8..737ac28 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
@@ -33,9 +33,8 @@
      * Pushes the collection of flow entries currently applied on the given
      * device.
      *
-     * @param deviceId device identifier
-     * @return collection of flow entries
+     * @param flowRules collection of flow rules
      */
-    void pushFlowMetrics(Iterable<FlowRule> flowEntries);
+    void pushFlowMetrics(Iterable<FlowRule> flowRules);
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
index 9a5fffa..2cfb4de 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
@@ -31,7 +31,7 @@
      * Creates a match on ETH_SRC field using the specified value. This value
      * may be a wildcard mask.
      *
-     * @param macValue MAC address value or wildcard mask
+     * @param mac MAC address value or wildcard mask
      * @return match criterion
      */
     public static Criterion matchEthSrc(MacAddress mac) {
@@ -42,7 +42,7 @@
      * Creates a match on ETH_DST field using the specified value. This value
      * may be a wildcard mask.
      *
-     * @param macValue MAC address value or wildcard mask
+     * @param mac MAC address value or wildcard mask
      * @return match criterion
      */
     public static Criterion matchEthDst(MacAddress mac) {
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
index 78eaaa9..a0ab04c 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
@@ -5,7 +5,6 @@
 
 /**
  * Abstraction of a single traffic treatment step.
- * @param <T> the type parameter for the instruction
  */
 public abstract class L2ModificationInstruction implements Instruction {
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
index cf25f10..797df9b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L3ModificationInstruction.java
@@ -4,7 +4,6 @@
 
 /**
  * Abstraction of a single traffic treatment step.
- * @param <T> the type parameter for the instruction
  */
 public abstract class L3ModificationInstruction implements Instruction {
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
new file mode 100644
index 0000000..cb5ed64
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
@@ -0,0 +1,102 @@
+package org.onlab.onos.net.host;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of end-station hosts. It may do so using whatever
+ * means are appropriate.
+ */
+public interface HostStore {
+
+    /**
+     * Creates a new host or updates the existing one based on the specified
+     * description.
+     *
+     * @param providerId      provider identification
+     * @param hostId          host identification
+     * @param hostDescription host description data
+     * @return appropriate event or null if no change resulted
+     */
+    HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
+                                 HostDescription hostDescription);
+
+    /**
+     * Removes the specified host from the inventory.
+     *
+     * @param hostId host identification
+     * @return remove event or null if host was not found
+     */
+    HostEvent removeHost(HostId hostId);
+
+    /**
+     * Returns the number of hosts in the store.
+     *
+     * @return host count
+     */
+    int getHostCount();
+
+    /**
+     * Returns a collection of all hosts in the store.
+     *
+     * @return iterable collection of all hosts
+     */
+    Iterable<Host> getHosts();
+
+    /**
+     * Returns the host with the specified identifer.
+     *
+     * @param hostId host identification
+     * @return host or null if not found
+     */
+    Host getHost(HostId hostId);
+
+    /**
+     * Returns the set of all hosts within the specified VLAN.
+     *
+     * @param vlanId vlan id
+     * @return set of hosts in the vlan
+     */
+    Set<Host> getHosts(VlanId vlanId);
+
+    /**
+     * Returns the set of hosts with the specified MAC address.
+     *
+     * @param mac mac address
+     * @return set of hosts with the given mac
+     */
+    Set<Host> getHosts(MacAddress mac);
+
+    /**
+     * Returns the set of hosts with the specified IP address.
+     *
+     * @param ip ip address
+     * @return set of hosts with the given IP
+     */
+    Set<Host> getHosts(IpAddress ip);
+
+    /**
+     * Returns the set of hosts whose location falls on the given connection point.
+     *
+     * @param connectPoint connection point
+     * @return set of hosts
+     */
+    Set<Host> getConnectedHosts(ConnectPoint connectPoint);
+
+    /**
+     * Returns the set of hosts whose location falls on the given device.
+     *
+     * @param deviceId infrastructure device identifier
+     * @return set of hosts
+     */
+    Set<Host> getConnectedHosts(DeviceId deviceId);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java
new file mode 100644
index 0000000..4391471
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java
@@ -0,0 +1,91 @@
+package org.onlab.onos.net.link;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.provider.ProviderId;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of infrastructure links. It may do so using whatever
+ * means are appropriate.
+ */
+public interface LinkStore {
+
+    /**
+     * Returns the number of links in the store.
+     *
+     * @return number of links
+     */
+    int getLinkCount();
+
+    /**
+     * Returns an iterable collection of all links in the inventory.
+     *
+     * @return collection of all links
+     */
+    Iterable<Link> getLinks();
+
+    /**
+     * Returns all links egressing from the specified device.
+     *
+     * @param deviceId device identifier
+     * @return set of device links
+     */
+    Set<Link> getDeviceEgressLinks(DeviceId deviceId);
+
+    /**
+     * Returns all links ingressing from the specified device.
+     *
+     * @param deviceId device identifier
+     * @return set of device links
+     */
+    Set<Link> getDeviceIngressLinks(DeviceId deviceId);
+
+    /**
+     * Returns the link between the two end-points.
+     *
+     * @param src source connection point
+     * @param dst destination connection point
+     * @return link or null if one not found between the end-points
+     */
+    Link getLink(ConnectPoint src, ConnectPoint dst);
+
+    /**
+     * Returns all links egressing from the specified connection point.
+     *
+     * @param src source connection point
+     * @return set of connection point links
+     */
+    Set<Link> getEgressLinks(ConnectPoint src);
+
+    /**
+     * Returns all links ingressing to the specified connection point.
+     *
+     * @param dst destination connection point
+     * @return set of connection point links
+     */
+    Set<Link> getIngressLinks(ConnectPoint dst);
+
+    /**
+     * Creates a new link, or updates an existing one, based on the given
+     * information.
+     *
+     * @param providerId      provider identity
+     * @param linkDescription link description
+     * @return create or update link event, or null if no change resulted
+     */
+    public LinkEvent createOrUpdateLink(ProviderId providerId,
+                                        LinkDescription linkDescription);
+
+    /**
+     * Removes the link based on the specified information.
+     *
+     * @param src link source
+     * @param dst link destination
+     * @return remove link event, or null if no change resulted
+     */
+    LinkEvent removeLink(ConnectPoint src, ConnectPoint dst);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java
new file mode 100644
index 0000000..5e8b19d
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java
@@ -0,0 +1,129 @@
+package org.onlab.onos.net.topology;
+
+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 java.util.List;
+import java.util.Set;
+
+/**
+ * Manages inventory of topology snapshots. It may do so using whatever
+ * means appropriate.
+ */
+public interface TopologyStore {
+
+    /**
+     * Returns the current topology snapshot.
+     *
+     * @return current topology descriptor
+     */
+    Topology currentTopology();
+
+    /**
+     * Indicates whether the topology is the latest.
+     *
+     * @param topology topology descriptor
+     * @return true if topology is the most recent one
+     */
+    boolean isLatest(Topology topology);
+
+    /**
+     * Returns the immutable graph view of the current topology.
+     *
+     * @param topology topology descriptor
+     * @return graph view
+     */
+    TopologyGraph getGraph(Topology topology);
+
+    /**
+     * Returns the set of topology SCC clusters.
+     *
+     * @param topology topology descriptor
+     * @return set of clusters
+     */
+    Set<TopologyCluster> getClusters(Topology topology);
+
+    /**
+     * Returns the cluster of the specified topology.
+     *
+     * @param topology  topology descriptor
+     * @param clusterId cluster identity
+     * @return topology cluster
+     */
+    TopologyCluster getCluster(Topology topology, ClusterId clusterId);
+
+    /**
+     * Returns the cluster of the specified topology.
+     *
+     * @param topology topology descriptor
+     * @param cluster  topology cluster
+     * @return set of cluster links
+     */
+    Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster);
+
+    /**
+     * Returns the cluster of the specified topology.
+     *
+     * @param topology topology descriptor
+     * @param cluster  topology cluster
+     * @return set of cluster links
+     */
+    Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster);
+
+    /**
+     * Returns the set of pre-computed shortest paths between src and dest.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @return set of shortest paths
+     */
+    Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst);
+
+    /**
+     * Computes and returns the set of shortest paths between src and dest.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @param weight   link weight function
+     * @return set of shortest paths
+     */
+    Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+                       LinkWeight weight);
+
+    /**
+     * Indicates whether the given connect point is part of the network fabric.
+     *
+     * @param topology     topology descriptor
+     * @param connectPoint connection point
+     * @return true if infrastructure; false otherwise
+     */
+    boolean isInfrastructure(Topology topology, ConnectPoint connectPoint);
+
+    /**
+     * Indicates whether broadcast is allowed for traffic received on the
+     * given connection point.
+     *
+     * @param topology     topology descriptor
+     * @param connectPoint connection point
+     * @return true if broadcast allowed; false otherwise
+     */
+    boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint);
+
+    /**
+     * Generates a new topology snapshot from the specified description.
+     *
+     * @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(ProviderId providerId,
+                                 GraphDescription graphDescription,
+                                 List<Event> reasons);
+}
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/DeviceManager.java
similarity index 97%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManager.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/DeviceManager.java
index d037f2a..a73efd8 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/DeviceManager.java
@@ -21,6 +21,7 @@
 import org.onlab.onos.net.device.DeviceProviderRegistry;
 import org.onlab.onos.net.device.DeviceProviderService;
 import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.device.DeviceStore;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.AbstractProviderRegistry;
 import org.onlab.onos.net.provider.AbstractProviderService;
@@ -36,7 +37,7 @@
  */
 @Component(immediate = true)
 @Service
-public class SimpleDeviceManager
+public class DeviceManager
         extends AbstractProviderRegistry<DeviceProvider, DeviceProviderService>
         implements DeviceService, DeviceAdminService, DeviceProviderRegistry {
 
@@ -51,7 +52,8 @@
     protected final AbstractListenerRegistry<DeviceEvent, DeviceListener>
             listenerRegistry = new AbstractListenerRegistry<>();
 
-    private final SimpleDeviceStore store = new SimpleDeviceStore();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java
index ece82e1..30c506ff 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceStore.java
@@ -1,6 +1,10 @@
 package org.onlab.onos.net.trivial.device.impl;
 
 import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.net.DefaultDevice;
 import org.onlab.onos.net.DefaultPort;
 import org.onlab.onos.net.Device;
@@ -10,8 +14,10 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.device.DeviceDescription;
 import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceStore;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.ProviderId;
+import org.slf4j.Logger;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -26,12 +32,17 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Manages inventory of infrastructure DEVICES using trivial in-memory
  * structures implementation.
  */
-class SimpleDeviceStore {
+@Component(immediate = true)
+@Service
+public class SimpleDeviceStore implements DeviceStore {
+
+    private final Logger log = getLogger(getClass());
 
     public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
 
@@ -40,44 +51,33 @@
     private final Set<DeviceId> availableDevices = new HashSet<>();
     private final Map<DeviceId, Map<PortNumber, Port>> devicePorts = new HashMap<>();
 
-    /**
-     * Returns the number of devices known to the system.
-     *
-     * @return number of devices
-     */
-    int getDeviceCount() {
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public int getDeviceCount() {
         return devices.size();
     }
 
-    /**
-     * Returns an iterable collection of all devices known to the system.
-     *
-     * @return device collection
-     */
-    Iterable<Device> getDevices() {
+    @Override
+    public Iterable<Device> getDevices() {
         return Collections.unmodifiableSet(new HashSet<Device>(devices.values()));
     }
 
-    /**
-     * Returns the device with the specified identifier.
-     *
-     * @param deviceId device identifier
-     * @return device
-     */
-    Device getDevice(DeviceId deviceId) {
+    @Override
+    public Device getDevice(DeviceId deviceId) {
         return devices.get(deviceId);
     }
 
-    /**
-     * Creates a new infrastructure device, or updates an existing one using
-     * the supplied device description.
-     *
-     * @param providerId        provider identifier
-     * @param deviceId          device identifier
-     * @param deviceDescription device description
-     * @return ready to send event describing what occurred; null if no change
-     */
-    DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
+    @Override
+    public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
                                      DeviceDescription deviceDescription) {
         DefaultDevice device = devices.get(deviceId);
         if (device == null) {
@@ -130,13 +130,8 @@
         }
     }
 
-    /**
-     * Removes the specified infrastructure device.
-     *
-     * @param deviceId device identifier
-     * @return ready to send event describing what occurred; null if no change
-     */
-    DeviceEvent markOffline(DeviceId deviceId) {
+    @Override
+    public DeviceEvent markOffline(DeviceId deviceId) {
         synchronized (this) {
             Device device = devices.get(deviceId);
             boolean removed = device != null && availableDevices.remove(deviceId);
@@ -145,15 +140,8 @@
         }
     }
 
-    /**
-     * Updates the ports of the specified infrastructure device using the given
-     * list of port descriptions. The list is assumed to be comprehensive.
-     *
-     * @param deviceId         device identifier
-     * @param portDescriptions list of port descriptions
-     * @return ready to send events describing what occurred; empty list if no change
-     */
-    List<DeviceEvent> updatePorts(DeviceId deviceId,
+    @Override
+    public List<DeviceEvent> updatePorts(DeviceId deviceId,
                                   List<PortDescription> portDescriptions) {
         List<DeviceEvent> events = new ArrayList<>();
         synchronized (this) {
@@ -230,15 +218,8 @@
         return ports;
     }
 
-    /**
-     * Updates the port status of the specified infrastructure device using the
-     * given port description.
-     *
-     * @param deviceId        device identifier
-     * @param portDescription port description
-     * @return ready to send event describing what occurred; null if no change
-     */
-    DeviceEvent updatePortStatus(DeviceId deviceId,
+    @Override
+    public DeviceEvent updatePortStatus(DeviceId deviceId,
                                  PortDescription portDescription) {
         synchronized (this) {
             Device device = devices.get(deviceId);
@@ -249,58 +230,31 @@
         }
     }
 
-    /**
-     * Returns the list of ports that belong to the specified device.
-     *
-     * @param deviceId device identifier
-     * @return list of device ports
-     */
-    List<Port> getPorts(DeviceId deviceId) {
+    @Override
+    public List<Port> getPorts(DeviceId deviceId) {
         Map<PortNumber, Port> ports = devicePorts.get(deviceId);
         return ports == null ? new ArrayList<Port>() : ImmutableList.copyOf(ports.values());
     }
 
-    /**
-     * Returns the specified device port.
-     *
-     * @param deviceId   device identifier
-     * @param portNumber port number
-     * @return device port
-     */
-    Port getPort(DeviceId deviceId, PortNumber portNumber) {
+    @Override
+    public Port getPort(DeviceId deviceId, PortNumber portNumber) {
         Map<PortNumber, Port> ports = devicePorts.get(deviceId);
         return ports == null ? null : ports.get(portNumber);
     }
 
-    /**
-     * Indicates whether the specified device is available/online.
-     *
-     * @param deviceId device identifier
-     * @return true if device is available
-     */
-    boolean isAvailable(DeviceId deviceId) {
+    @Override
+    public boolean isAvailable(DeviceId deviceId) {
         return availableDevices.contains(deviceId);
     }
 
-    /**
-     * Returns the mastership role determined for this device.
-     *
-     * @param deviceId device identifier
-     * @return mastership role
-     */
-    MastershipRole getRole(DeviceId deviceId) {
+    @Override
+    public MastershipRole getRole(DeviceId deviceId) {
         MastershipRole role = roles.get(deviceId);
         return role != null ? role : MastershipRole.NONE;
     }
 
-    /**
-     * Administratively sets the role of the specified device.
-     *
-     * @param deviceId device identifier
-     * @param role     mastership role to apply
-     * @return mastership role change event or null if no change
-     */
-    DeviceEvent setRole(DeviceId deviceId, MastershipRole role) {
+    @Override
+    public DeviceEvent setRole(DeviceId deviceId, MastershipRole role) {
         synchronized (this) {
             Device device = getDevice(deviceId);
             checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
@@ -310,12 +264,8 @@
         }
     }
 
-    /**
-     * Administratively removes the specified device from the store.
-     *
-     * @param deviceId device to be removed
-     */
-    DeviceEvent removeDevice(DeviceId deviceId) {
+    @Override
+    public DeviceEvent removeDevice(DeviceId deviceId) {
         synchronized (this) {
             roles.remove(deviceId);
             Device device = devices.remove(deviceId);
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
index 43e06f5..9cf235b 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManager.java
@@ -20,6 +20,7 @@
 import org.onlab.onos.net.host.HostProviderRegistry;
 import org.onlab.onos.net.host.HostProviderService;
 import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.host.HostStore;
 import org.onlab.onos.net.provider.AbstractProviderRegistry;
 import org.onlab.onos.net.provider.AbstractProviderService;
 import org.onlab.packet.IpAddress;
@@ -47,7 +48,8 @@
     private final AbstractListenerRegistry<HostEvent, HostListener>
             listenerRegistry = new AbstractListenerRegistry<>();
 
-    private final SimpleHostStore store = new SimpleHostStore();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
index f1a64e7..1febb67 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
@@ -4,6 +4,7 @@
 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 static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -11,6 +12,10 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultHost;
 import org.onlab.onos.net.DeviceId;
@@ -18,6 +23,7 @@
 import org.onlab.onos.net.HostId;
 import org.onlab.onos.net.host.HostDescription;
 import org.onlab.onos.net.host.HostEvent;
+import org.onlab.onos.net.host.HostStore;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
@@ -26,29 +32,37 @@
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
+import org.slf4j.Logger;
 
 /**
  * Manages inventory of end-station hosts using trivial in-memory
  * implementation.
  */
-public class SimpleHostStore {
+@Component(immediate = true)
+@Service
+public class SimpleHostStore implements HostStore {
 
+    private final Logger log = getLogger(getClass());
+
+    // Host inventory
     private final Map<HostId, Host> hosts = new ConcurrentHashMap<>();
 
-    // hosts sorted based on their location
+    // Hosts tracked by their location
     private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
 
-    /**
-     * Creates a new host or updates the existing one based on the specified
-     * description.
-     *
-     * @param providerId      provider identification
-     * @param hostId          host identification
-     * @param hostDescription host description data
-     * @return appropriate event or null if no change resulted
-     */
-    HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
-                                 HostDescription hostDescription) {
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
+                                        HostDescription hostDescription) {
         Host host = hosts.get(hostId);
         if (host == null) {
             return createHost(providerId, hostId, hostDescription);
@@ -102,13 +116,8 @@
         return event;
     }
 
-    /**
-     * Removes the specified host from the inventory.
-     *
-     * @param hostId host identification
-     * @return remove event or null if host was not found
-     */
-    HostEvent removeHost(HostId hostId) {
+    @Override
+    public HostEvent removeHost(HostId hostId) {
         synchronized (this) {
             Host host = hosts.remove(hostId);
             if (host != null) {
@@ -119,41 +128,23 @@
         }
     }
 
-    /**
-     * Returns the number of hosts in the store.
-     *
-     * @return host count
-     */
-    int getHostCount() {
+    @Override
+    public int getHostCount() {
         return hosts.size();
     }
 
-    /**
-     * Returns a collection of all hosts in the store.
-     *
-     * @return iterable collection of all hosts
-     */
-    Iterable<Host> getHosts() {
+    @Override
+    public Iterable<Host> getHosts() {
         return Collections.unmodifiableSet(new HashSet<>(hosts.values()));
     }
 
-    /**
-     * Returns the host with the specified identifer.
-     *
-     * @param hostId host identification
-     * @return host or null if not found
-     */
-    Host getHost(HostId hostId) {
+    @Override
+    public Host getHost(HostId hostId) {
         return hosts.get(hostId);
     }
 
-    /**
-     * Returns the set of all hosts within the specified VLAN.
-     *
-     * @param vlanId vlan id
-     * @return set of hosts in the vlan
-     */
-    Set<Host> getHosts(VlanId vlanId) {
+    @Override
+    public Set<Host> getHosts(VlanId vlanId) {
         Set<Host> vlanset = new HashSet<>();
         for (Host h : hosts.values()) {
             if (h.vlan().equals(vlanId)) {
@@ -163,13 +154,8 @@
         return vlanset;
     }
 
-    /**
-     * Returns the set of hosts with the specified MAC address.
-     *
-     * @param mac mac address
-     * @return set of hosts with the given mac
-     */
-    Set<Host> getHosts(MacAddress mac) {
+    @Override
+    public Set<Host> getHosts(MacAddress mac) {
         Set<Host> macset = new HashSet<>();
         for (Host h : hosts.values()) {
             if (h.mac().equals(mac)) {
@@ -179,13 +165,8 @@
         return macset;
     }
 
-    /**
-     * Returns the set of hosts with the specified IP address.
-     *
-     * @param ip ip address
-     * @return set of hosts with the given IP
-     */
-    Set<Host> getHosts(IpAddress ip) {
+    @Override
+    public Set<Host> getHosts(IpAddress ip) {
         Set<Host> ipset = new HashSet<>();
         for (Host h : hosts.values()) {
             if (h.ipAddresses().contains(ip)) {
@@ -195,22 +176,12 @@
         return ipset;
     }
 
-    /**
-     * Returns the set of hosts whose location falls on the given connection point.
-     *
-     * @param connectPoint connection point
-     * @return set of hosts
-     */
-    Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+    @Override
+    public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
         return ImmutableSet.copyOf(locations.get(connectPoint));
     }
 
-    /**
-     * Returns the set of hosts whose location falls on the given device.
-     *
-     * @param deviceId infrastructure device identifier
-     * @return set of hosts
-     */
+    @Override
     public Set<Host> getConnectedHosts(DeviceId deviceId) {
         Set<Host> hostset = new HashSet<>();
         for (ConnectPoint p : locations.keySet()) {
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/LinkManager.java
similarity index 97%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/LinkManager.java
index 7242642..13cb546 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/LinkManager.java
@@ -27,6 +27,7 @@
 import org.onlab.onos.net.link.LinkProviderRegistry;
 import org.onlab.onos.net.link.LinkProviderService;
 import org.onlab.onos.net.link.LinkService;
+import org.onlab.onos.net.link.LinkStore;
 import org.onlab.onos.net.provider.AbstractProviderRegistry;
 import org.onlab.onos.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
@@ -38,7 +39,7 @@
  */
 @Component(immediate = true)
 @Service
-public class SimpleLinkManager
+public class LinkManager
         extends AbstractProviderRegistry<LinkProvider, LinkProviderService>
         implements LinkService, LinkAdminService, LinkProviderRegistry {
 
@@ -51,10 +52,12 @@
     protected final AbstractListenerRegistry<LinkEvent, LinkListener>
             listenerRegistry = new AbstractListenerRegistry<>();
 
-    private final SimpleLinkStore store = new SimpleLinkStore();
     private final DeviceListener deviceListener = new InnerDeviceListener();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkStore store;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkStore.java
index 2ba7a30..935e5dc 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkStore.java
@@ -3,13 +3,19 @@
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultLink;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.link.LinkDescription;
 import org.onlab.onos.net.link.LinkEvent;
+import org.onlab.onos.net.link.LinkStore;
 import org.onlab.onos.net.provider.ProviderId;
+import org.slf4j.Logger;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -20,15 +26,18 @@
 
 import static org.onlab.onos.net.Link.Type.DIRECT;
 import static org.onlab.onos.net.Link.Type.INDIRECT;
-import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
-import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
-import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED;
+import static org.onlab.onos.net.link.LinkEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Manages inventory of infrastructure links using trivial in-memory structures
  * implementation.
  */
-class SimpleLinkStore {
+@Component(immediate = true)
+@Service
+public class SimpleLinkStore implements LinkStore {
+
+    private final Logger log = getLogger(getClass());
 
     // Link inventory
     private final Map<LinkKey, DefaultLink> links = new ConcurrentHashMap<>();
@@ -37,64 +46,45 @@
     private final Multimap<DeviceId, Link> srcLinks = HashMultimap.create();
     private final Multimap<DeviceId, Link> dstLinks = HashMultimap.create();
 
-    private static final Set<Link> EMPTY = ImmutableSet.copyOf(new Link[]{});
+    private static final Set<Link> EMPTY = ImmutableSet.of();
 
-    /**
-     * Returns the number of links in the store.
-     *
-     * @return number of links
-     */
-    int getLinkCount() {
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public int getLinkCount() {
         return links.size();
     }
 
-    /**
-     * Returns an iterable collection of all links in the inventory.
-     *
-     * @return collection of all links
-     */
-    Iterable<Link> getLinks() {
+    @Override
+    public Iterable<Link> getLinks() {
         return Collections.unmodifiableSet(new HashSet<Link>(links.values()));
     }
 
-    /**
-     * Returns all links egressing from the specified device.
-     *
-     * @param deviceId device identifier
-     * @return set of device links
-     */
-    Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
+    @Override
+    public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
         return ImmutableSet.copyOf(srcLinks.get(deviceId));
     }
 
-    /**
-     * Returns all links ingressing from the specified device.
-     *
-     * @param deviceId device identifier
-     * @return set of device links
-     */
-    Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
+    @Override
+    public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
         return ImmutableSet.copyOf(dstLinks.get(deviceId));
     }
 
-    /**
-     * Returns the link between the two end-points.
-     *
-     * @param src source connection point
-     * @param dst destination connection point
-     * @return link or null if one not found between the end-points
-     */
-    Link getLink(ConnectPoint src, ConnectPoint dst) {
+    @Override
+    public Link getLink(ConnectPoint src, ConnectPoint dst) {
         return links.get(new LinkKey(src, dst));
     }
 
-    /**
-     * Returns all links egressing from the specified connection point.
-     *
-     * @param src source connection point
-     * @return set of connection point links
-     */
-    Set<Link> getEgressLinks(ConnectPoint src) {
+    @Override
+    public Set<Link> getEgressLinks(ConnectPoint src) {
         Set<Link> egress = new HashSet<>();
         for (Link link : srcLinks.get(src.deviceId())) {
             if (link.src().equals(src)) {
@@ -104,13 +94,8 @@
         return egress;
     }
 
-    /**
-     * Returns all links ingressing to the specified connection point.
-     *
-     * @param dst destination connection point
-     * @return set of connection point links
-     */
-    Set<Link> getIngressLinks(ConnectPoint dst) {
+    @Override
+    public Set<Link> getIngressLinks(ConnectPoint dst) {
         Set<Link> ingress = new HashSet<>();
         for (Link link : dstLinks.get(dst.deviceId())) {
             if (link.dst().equals(dst)) {
@@ -120,14 +105,7 @@
         return ingress;
     }
 
-    /**
-     * Creates a new link, or updates an existing one, based on the given
-     * information.
-     *
-     * @param providerId      provider identity
-     * @param linkDescription link description
-     * @return create or update link event, or null if no change resulted
-     */
+    @Override
     public LinkEvent createOrUpdateLink(ProviderId providerId,
                                         LinkDescription linkDescription) {
         LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst());
@@ -171,14 +149,8 @@
         return null;
     }
 
-    /**
-     * Removes the link based on the specified information.
-     *
-     * @param src link source
-     * @param dst link destination
-     * @return remove link event, or null if no change resulted
-     */
-    LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
+    @Override
+    public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
         synchronized (this) {
             Link link = links.remove(new LinkKey(src, dst));
             if (link != null) {
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/PathManager.java
similarity index 98%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/PathManager.java
index 5fe7793..24ee580 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimplePathManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/PathManager.java
@@ -41,7 +41,7 @@
  */
 @Component(immediate = true)
 @Service
-public class SimplePathManager implements PathService {
+public class PathManager implements PathService {
 
     private static final String ELEMENT_ID_NULL = "Element ID cannot be null";
 
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 59360c2..c837eb7 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,5 +1,9 @@
 package org.onlab.onos.net.trivial.topology.impl;
 
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.event.Event;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
@@ -13,151 +17,96 @@
 import org.onlab.onos.net.topology.TopologyCluster;
 import org.onlab.onos.net.topology.TopologyEvent;
 import org.onlab.onos.net.topology.TopologyGraph;
+import org.onlab.onos.net.topology.TopologyStore;
+import org.slf4j.Logger;
 
 import java.util.List;
 import java.util.Set;
 
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Manages inventory of topology snapshots using trivial in-memory
  * structures implementation.
  */
-class SimpleTopologyStore {
+@Component(immediate = true)
+@Service
+public class SimpleTopologyStore implements TopologyStore {
+
+    private final Logger log = getLogger(getClass());
 
     private volatile DefaultTopology current;
 
-    /**
-     * Returns the current topology snapshot.
-     *
-     * @return current topology descriptor
-     */
-    Topology currentTopology() {
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+    @Override
+    public Topology currentTopology() {
         return current;
     }
 
-    /**
-     * Indicates whether the topology is the latest.
-     *
-     * @param topology topology descriptor
-     * @return true if topology is the most recent one
-     */
-    boolean isLatest(Topology topology) {
+    @Override
+    public boolean isLatest(Topology topology) {
         // Topology is current only if it is the same as our current topology
         return topology == current;
     }
 
-    /**
-     * Returns the immutable graph view of the current topology.
-     *
-     * @param topology topology descriptor
-     * @return graph view
-     */
-    TopologyGraph getGraph(DefaultTopology topology) {
-        return topology.getGraph();
+    @Override
+    public TopologyGraph getGraph(Topology topology) {
+        return defaultTopology(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();
+    @Override
+    public Set<TopologyCluster> getClusters(Topology topology) {
+        return defaultTopology(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);
+    @Override
+    public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
+        return defaultTopology(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);
+    @Override
+    public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
+        return defaultTopology(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);
+    @Override
+    public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
+        return defaultTopology(topology).getClusterLinks(cluster);
     }
 
-    /**
-     * Returns the set of pre-computed shortest paths between src and dest.
-     *
-     * @param topology topology descriptor
-     * @param src      source device
-     * @param dst      destination device
-     * @return set of shortest paths
-     */
-    Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst) {
-        return topology.getPaths(src, dst);
+    @Override
+    public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
+        return defaultTopology(topology).getPaths(src, dst);
     }
 
-    /**
-     * Computes and returns the set of shortest paths between src and dest.
-     *
-     * @param topology topology descriptor
-     * @param src      source device
-     * @param dst      destination device
-     * @param weight   link weight function
-     * @return set of shortest paths
-     */
-    Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst,
-                       LinkWeight weight) {
-        return topology.getPaths(src, dst, weight);
+    @Override
+    public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+                              LinkWeight weight) {
+        return defaultTopology(topology).getPaths(src, dst, weight);
     }
 
-    /**
-     * Indicates whether the given connect point is part of the network fabric.
-     *
-     * @param topology     topology descriptor
-     * @param connectPoint connection point
-     * @return true if infrastructure; false otherwise
-     */
-    boolean isInfrastructure(DefaultTopology topology, ConnectPoint connectPoint) {
-        return topology.isInfrastructure(connectPoint);
+    @Override
+    public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
+        return defaultTopology(topology).isInfrastructure(connectPoint);
     }
 
-    /**
-     * Indicates whether broadcast is allowed for traffic received on the
-     * given connection point.
-     *
-     * @param topology     topology descriptor
-     * @param connectPoint connection point
-     * @return true if broadcast allowed; false otherwise
-     */
-    boolean isBroadcastPoint(DefaultTopology topology, ConnectPoint connectPoint) {
-        return topology.isBroadcastPoint(connectPoint);
+    @Override
+    public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
+        return defaultTopology(topology).isBroadcastPoint(connectPoint);
     }
 
-    /**
-     * Generates a new topology snapshot from the specified description.
-     *
-     * @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(ProviderId providerId,
-                                 GraphDescription graphDescription,
-                                 List<Event> reasons) {
+    @Override
+    public TopologyEvent updateTopology(ProviderId providerId,
+                                        GraphDescription graphDescription,
+                                        List<Event> reasons) {
         // First off, make sure that what we're given is indeed newer than
         // what we already have.
         if (current != null && graphDescription.timestamp() < current.time()) {
@@ -175,4 +124,13 @@
         }
     }
 
+    // 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");
+    }
+
 }
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/TopologyManager.java
similarity index 83%
rename from core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
rename to core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/TopologyManager.java
index 159dbb0..b6614a3 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/TopologyManager.java
@@ -27,6 +27,7 @@
 import org.onlab.onos.net.topology.TopologyProviderRegistry;
 import org.onlab.onos.net.topology.TopologyProviderService;
 import org.onlab.onos.net.topology.TopologyService;
+import org.onlab.onos.net.topology.TopologyStore;
 import org.slf4j.Logger;
 
 import java.util.List;
@@ -40,7 +41,7 @@
  */
 @Component(immediate = true)
 @Service
-public class SimpleTopologyManager
+public class TopologyManager
         extends AbstractProviderRegistry<TopologyProvider, TopologyProviderService>
         implements TopologyService, TopologyProviderRegistry {
 
@@ -55,7 +56,8 @@
     private final AbstractListenerRegistry<TopologyEvent, TopologyListener>
             listenerRegistry = new AbstractListenerRegistry<>();
 
-    private final SimpleTopologyStore store = new SimpleTopologyStore();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TopologyStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
@@ -81,49 +83,40 @@
     @Override
     public boolean isLatest(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        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");
+        return store.isLatest(topology);
     }
 
     @Override
     public Set<TopologyCluster> getClusters(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.getClusters(defaultTopology(topology));
+        return store.getClusters(topology);
     }
 
     @Override
     public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(topology, CLUSTER_ID_NULL);
-        return store.getCluster(defaultTopology(topology), clusterId);
+        return store.getCluster(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);
+        return store.getClusterDevices(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);
+        return store.getClusterLinks(topology, cluster);
     }
 
     @Override
     public TopologyGraph getGraph(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.getGraph(defaultTopology(topology));
+        return store.getGraph(topology);
     }
 
     @Override
@@ -131,7 +124,7 @@
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
-        return store.getPaths(defaultTopology(topology), src, dst);
+        return store.getPaths(topology, src, dst);
     }
 
     @Override
@@ -140,21 +133,21 @@
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
         checkNotNull(weight, "Link weight cannot be null");
-        return store.getPaths(defaultTopology(topology), src, dst, weight);
+        return store.getPaths(topology, src, dst, weight);
     }
 
     @Override
     public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(connectPoint, CONNECTION_POINT_NULL);
-        return store.isInfrastructure(defaultTopology(topology), connectPoint);
+        return store.isInfrastructure(topology, connectPoint);
     }
 
     @Override
     public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(connectPoint, CONNECTION_POINT_NULL);
-        return store.isBroadcastPoint(defaultTopology(topology), connectPoint);
+        return store.isBroadcastPoint(topology, connectPoint);
     }
 
     @Override
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/DeviceManagerTest.java
similarity index 98%
rename from core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java
rename to core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/DeviceManagerTest.java
index 4d531d3..dccfec4 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/SimpleDeviceManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/device/impl/DeviceManagerTest.java
@@ -36,7 +36,7 @@
 /**
  * Test codifying the device service & device provider service contracts.
  */
-public class SimpleDeviceManagerTest {
+public class DeviceManagerTest {
 
     private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
@@ -51,8 +51,7 @@
     private static final PortNumber P2 = PortNumber.portNumber(2);
     private static final PortNumber P3 = PortNumber.portNumber(3);
 
-
-    private SimpleDeviceManager mgr;
+    private DeviceManager mgr;
 
     protected DeviceService service;
     protected DeviceAdminService admin;
@@ -63,10 +62,11 @@
 
     @Before
     public void setUp() {
-        mgr = new SimpleDeviceManager();
+        mgr = new DeviceManager();
         service = mgr;
         admin = mgr;
         registry = mgr;
+        mgr.store = new SimpleDeviceStore();
         mgr.eventDispatcher = new TestEventDispatcher();
         mgr.activate();
 
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java
index 67c14b0..1ee61df 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/host/impl/SimpleHostManagerTest.java
@@ -73,6 +73,7 @@
     @Before
     public void setUp() {
         mgr = new SimpleHostManager();
+        mgr.store = new SimpleHostStore();
         mgr.eventDispatcher = new TestEventDispatcher();
         registry = mgr;
         mgr.activate();
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/LinkManagerTest.java
similarity index 97%
rename from core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java
rename to core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/LinkManagerTest.java
index 93610b5..f7aeb08 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/SimpleLinkManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/link/impl/LinkManagerTest.java
@@ -22,7 +22,7 @@
 import org.onlab.onos.net.provider.AbstractProvider;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.onos.event.impl.TestEventDispatcher;
-import org.onlab.onos.net.trivial.device.impl.SimpleDeviceManager;
+import org.onlab.onos.net.trivial.device.impl.DeviceManager;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -38,7 +38,7 @@
 /**
  * Test codifying the link service & link provider service contracts.
  */
-public class SimpleLinkManagerTest {
+public class LinkManagerTest {
 
     private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
@@ -50,7 +50,7 @@
     private static final PortNumber P3 = PortNumber.portNumber(3);
 
 
-    private SimpleLinkManager mgr;
+    private LinkManager mgr;
 
     protected LinkService service;
     protected LinkAdminService admin;
@@ -61,12 +61,13 @@
 
     @Before
     public void setUp() {
-        mgr = new SimpleLinkManager();
+        mgr = new LinkManager();
         service = mgr;
         admin = mgr;
         registry = mgr;
+        mgr.store = new SimpleLinkStore();
         mgr.eventDispatcher = new TestEventDispatcher();
-        mgr.deviceService = new SimpleDeviceManager();
+        mgr.deviceService = new DeviceManager();
         mgr.activate();
 
         service.addListener(listener);
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProviderTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProviderTest.java
index 920efe3..a4cf5b5 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProviderTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyProviderTest.java
@@ -15,8 +15,8 @@
 import org.onlab.onos.net.topology.TopologyProvider;
 import org.onlab.onos.net.topology.TopologyProviderRegistry;
 import org.onlab.onos.net.topology.TopologyProviderService;
-import org.onlab.onos.net.trivial.device.impl.SimpleDeviceManager;
-import org.onlab.onos.net.trivial.link.impl.SimpleLinkManager;
+import org.onlab.onos.net.trivial.device.impl.DeviceManager;
+import org.onlab.onos.net.trivial.link.impl.LinkManager;
 
 import java.util.List;
 import java.util.Set;
@@ -27,8 +27,8 @@
 import static org.onlab.junit.TestTools.assertAfter;
 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
 import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
-import static org.onlab.onos.net.trivial.topology.impl.SimpleTopologyManagerTest.device;
-import static org.onlab.onos.net.trivial.topology.impl.SimpleTopologyManagerTest.link;
+import static org.onlab.onos.net.trivial.topology.impl.TopologyManagerTest.device;
+import static org.onlab.onos.net.trivial.topology.impl.TopologyManagerTest.link;
 
 /**
  * Test of the default topology provider implementation.
@@ -122,7 +122,7 @@
         }
     }
 
-    private class TestDeviceService extends SimpleDeviceManager {
+    private class TestDeviceService extends DeviceManager {
         TestDeviceService() {
             eventDispatcher = new TestEventDispatcher();
             eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
@@ -140,7 +140,7 @@
         }
     }
 
-    private class TestLinkService extends SimpleLinkManager {
+    private class TestLinkService extends LinkManager {
         TestLinkService() {
             eventDispatcher = new TestEventDispatcher();
             eventDispatcher.addSink(LinkEvent.class, listenerRegistry);
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java
index 1fe33f6..3051dc3 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopologyTest.java
@@ -21,8 +21,8 @@
 import static org.junit.Assert.*;
 import static org.onlab.onos.net.DeviceId.deviceId;
 import static org.onlab.onos.net.PortNumber.portNumber;
-import static org.onlab.onos.net.trivial.topology.impl.SimpleTopologyManagerTest.device;
-import static org.onlab.onos.net.trivial.topology.impl.SimpleTopologyManagerTest.link;
+import static org.onlab.onos.net.trivial.topology.impl.TopologyManagerTest.device;
+import static org.onlab.onos.net.trivial.topology.impl.TopologyManagerTest.link;
 
 /**
  * Test of the default topology implementation.
diff --git a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/TopologyManagerTest.java
similarity index 98%
rename from core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java
rename to core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/TopologyManagerTest.java
index b0b1eed..06a37fc 100644
--- a/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManagerTest.java
+++ b/core/trivial/src/test/java/org/onlab/onos/net/trivial/topology/impl/TopologyManagerTest.java
@@ -41,11 +41,11 @@
 /**
  * Test of the topology subsystem.
  */
-public class SimpleTopologyManagerTest {
+public class TopologyManagerTest {
 
     private static final ProviderId PID = new ProviderId("of", "foo");
 
-    private SimpleTopologyManager mgr;
+    private TopologyManager mgr;
 
     protected TopologyService service;
     protected TopologyProviderRegistry registry;
@@ -55,10 +55,11 @@
 
     @Before
     public void setUp() {
-        mgr = new SimpleTopologyManager();
+        mgr = new TopologyManager();
         service = mgr;
         registry = mgr;
 
+        mgr.store = new SimpleTopologyStore();
         mgr.eventDispatcher = new TestEventDispatcher();
         mgr.activate();
 
diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html
index 1edf587..1c66548 100644
--- a/src/main/javadoc/overview.html
+++ b/src/main/javadoc/overview.html
@@ -28,7 +28,7 @@
     <br>
     <img src="doc-files/onos-subsystem.png" alt="ONOS subsystem structure"><br>
     For example, the device-subsystem comprises of a core
-    {@link org.onlab.onos.net.trivial.device.impl.SimpleDeviceManager DeviceManager},
+    {@link org.onlab.onos.net.trivial.device.impl.DeviceManager},
     which exposes a north-bound
     {@link org.onlab.onos.net.device.DeviceService} through which applications or other core components
     can learn about the global infrastructure device inventory and through
@@ -39,7 +39,7 @@
 </p>
 
 <p>
-    On the south-bound side, the core {@link org.onlab.onos.net.trivial.device.impl.SimpleDeviceManager DeviceManager}
+    On the south-bound side, the core {@link org.onlab.onos.net.trivial.device.impl.DeviceManager}
     exposes a {@link org.onlab.onos.net.device.DeviceProviderRegistry} through which any number of
     {@link org.onlab.onos.net.device.DeviceProvider} entities can register and in turn obtain a
     {@link org.onlab.onos.net.device.DeviceProviderService}. Device and port information can then be
@@ -52,7 +52,7 @@
 <p>
     Within the core, the tasks of indexing, persisting and synchronizing the
     global device and port state with the cluster peers falls on the
-    {@link org.onlab.onos.net.trivial.device.impl.SimpleDeviceStore DeviceStore}.
+    {@link org.onlab.onos.net.device.DeviceStore}.
 </p>
 
 <p>
diff --git a/tools/build/onos-package b/tools/build/onos-package
index ae78091..d35a48b 100755
--- a/tools/build/onos-package
+++ b/tools/build/onos-package
@@ -50,8 +50,8 @@
     $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
 
 # Patch the Apache Karaf distribution file to load ONOS features
-perl -pi.old -e 's|^(featuresBoot=.*)|\1,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-tvue,onos-app-fwd|' \
-    $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
+#perl -pi.old -e 's|^(featuresBoot=.*)|\1,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-tvue,onos-app-fwd|' \
+#    $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 
 
 # Patch the Apache Karaf distribution with ONOS branding bundle
 cp $M2_REPO/org/onlab/onos/onos-branding/$ONOS_VERSION/onos-branding-*.jar \
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index de0ed1e..f6c427a 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -45,20 +45,35 @@
 
 # Test related conveniences
 
-# Default virtual box ONOS instances 1,2 & 3
-export OC1="192.168.56.101"
-export OC2="192.168.56.102"
-export OC3="192.168.56.103"
-
-# Default instance is #1
-export OCI="$OC1"
-
 # SSH to a specified ONOS instance
 function sshctl {
     [ -n "$1" ] && OCI=$1 && shift
     ssh -Y sdn@$OCI "$@"
 }
 
+# Applies the settings in the specified cell file or lists current cell definition
+# if no cell file is given.
+function cell {
+    if [ -n "$1" ]; then
+        [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
+            echo "No such cell: $1" >&2 && return 1
+        . $ONOS_ROOT/tools/test/cells/$1
+        export OCI=$OC1
+        export ONOS_CELL=$1
+        cell
+    else
+        env | egrep "ONOS_CELL"
+        env | egrep "OCI"
+        env | egrep "OC[0-9]+" | sort
+    fi
+}
+
+cell local >/dev/null  # Default cell is the local VMs
+
+# Lists available cells
+function cells {
+    ls -1 $ONOS_ROOT/tools/test/cells
+}
 
 # Miscellaneous
 function spy {
diff --git a/tools/package/etc/hazelcast.xml b/tools/package/etc/hazelcast.xml
index 5cb41ba..abd6223 100644
--- a/tools/package/etc/hazelcast.xml
+++ b/tools/package/etc/hazelcast.xml
@@ -38,7 +38,7 @@
             </aws>
         </join>
         <interfaces enabled="true">
-            <interface>10.1.9.*</interface>
+            <interface>192.168.56.*</interface>
         </interfaces>
         <ssl enabled="false"/>
         <socket-interceptor enabled="false"/>
diff --git a/tools/test/bin/onos-install b/tools/test/bin/onos-install
index 9e17c76..d8e0522 100755
--- a/tools/test/bin/onos-install
+++ b/tools/test/bin/onos-install
@@ -7,22 +7,60 @@
 . $ONOS_ROOT/tools/build/envDefaults
 
 # If the first option is -f attempt uninstall first.
-[ "$1" = "-f" ] && shift && onos-uninstall ${1:-$OCI}
+[ "$1" = "-f" ] && shift && echo 'Uninstalling...' && onos-uninstall ${1:-$OCI}
 
 remote=$ONOS_USER@${1:-$OCI}
 
 scp -q $ONOS_TAR $remote:/tmp
 
+LOG=$ONOS_INSTALL_DIR/install.log
+onos=$ONOS_INSTALL_DIR/bin/onos
+
 ssh $remote "
     [ -d $ONOS_INSTALL_DIR/bin ] && echo \"ONOS is already installed\" && exit 1
 
     # Prepare a landing zone and unroll the bits
+    echo 'Unpacking...'
     sudo mkdir -p $ONOS_INSTALL_DIR && sudo chown sdn:sdn $ONOS_INSTALL_DIR
     tar zxmf /tmp/$ONOS_BITS.tar.gz -C $ONOS_INSTALL_DIR --strip-components=1
 
     # Make a link to the log file directory.
-    ln -s /opt/onos/$KARAF_DIST/data/log /opt/onos/log
+    ln -s $ONOS_INSTALL_DIR/$KARAF_DIST/data/log /opt/onos/log
     
     # TODO: Setup ONOS to run as a daemon; for now we at least startup
-    nohup /opt/onos/bin/onos-ctl server </dev/null 1>/opt/onos/svc.log 2>&1 &
+    echo 'Starting...'
+    nohup $ONOS_INSTALL_DIR/bin/onos-ctl server </dev/null | 1>/opt/onos/svc.log 2>&1 &
+
+    # Wait until we reach the run-level 100
+    echo 'Waiting for cluster bootstrap...'
+    running=""
+    while [ -z \$running ]; do
+        $onos bundle:list 2>>$LOG | grep -q 'START LEVEL 100' && running=1 || sleep 2
+    done
+
+    # Now create group onos and join it, while quitting the default one
+    if ! $onos cluster:group-list 2>>$LOG | cut -d \\  -f3 | grep -q onos; then
+        echo 'Creating ONOS group...'
+        installRole=primary
+        $onos cluster:group-create onos 1>>$LOG 2>&1
+    fi
+
+    echo 'Configuring group membership...'
+    node=\$($onos cluster:node-list 2>>$LOG | grep '^x' | cut -d \\  -f3)
+    $onos cluster:group-join onos \$node 1>>$LOG 2>&1
+    $onos cluster:group-quit default \$node 1>>$LOG 2>&1
+
+    if [ X\$installRole = Xprimary ]; then
+        echo 'Installing ONOS bundles...'
+        $onos cluster:feature-install onos onos-api 1>>$LOG 2>&1
+        $onos cluster:feature-install onos onos-core 1>>$LOG 2>&1
+        $onos cluster:feature-install onos onos-openflow 1>>$LOG 2>&1
+        $onos cluster:feature-install onoe onos-cli 1>>$LOG 2>&1
+      # $onos cluster:feature-install onos onos-gui 1>>$LOG 2>&1
+      # $onos cluster:feature-install onos onos-rest 1>>$LOG 2>&1
+        $onos cluster:feature-install onos onos-app-tvue 1>>$LOG 2>&1
+        $onos cluster:feature-install onos onos-app-fwd 1>>$LOG 2>&1
+    fi
+ 
+    echo 'Started...'
 "
diff --git a/tools/test/cells/local b/tools/test/cells/local
new file mode 100644
index 0000000..37911dd
--- /dev/null
+++ b/tools/test/cells/local
@@ -0,0 +1,7 @@
+# Default virtual box ONOS instances 1,2 & 3
+
+export OC1="192.168.56.101"
+export OC2="192.168.56.102"
+export OC3="192.168.56.103"
+
+
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
index a10e710..73bb9c9 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -234,6 +234,30 @@
         return mask() != 0;
     }
 
+    /**
+     * Determines whether a given address is contained within this IpAddress'
+     * network.
+     *
+     * @param other another IP address that could be contained in this network
+     * @return true if the other IP address is contained in this address'
+     * network, otherwise false
+     */
+    public boolean contains(IpAddress other) {
+        if (this.netmask <= other.netmask) {
+            // Special case where they're both /32 addresses
+            if (this.netmask == MAX_INET_MASK) {
+                return Arrays.equals(octets, other.octets);
+            }
+
+            // Mask the other address with our network mask
+            IpAddress otherMasked =
+                    IpAddress.valueOf(other.octets, netmask).network();
+
+            return network().equals(otherMasked);
+        }
+        return false;
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/utils/misc/src/test/java/org/onlab/packet/IPAddressTest.java b/utils/misc/src/test/java/org/onlab/packet/IPAddressTest.java
index f1a7b0d..d503229 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IPAddressTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IPAddressTest.java
@@ -1,6 +1,7 @@
 package org.onlab.packet;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
@@ -73,4 +74,26 @@
         assertTrue("incorrect netmask",
                 Arrays.equals(IpAddress.ANY, ip2.netmask().toOctets()));
     }
+
+    @Test
+    public void testContains() {
+        IpAddress slash31 = IpAddress.valueOf(BYTES1, 31);
+        IpAddress slash32 = IpAddress.valueOf(BYTES1, 32);
+        IpAddress differentSlash32 = IpAddress.valueOf(BYTES2, 32);
+
+        assertTrue(slash31.contains(differentSlash32));
+        assertFalse(differentSlash32.contains(slash31));
+
+        assertTrue(slash31.contains(slash32));
+        assertFalse(slash32.contains(differentSlash32));
+        assertFalse(differentSlash32.contains(slash32));
+
+        IpAddress zero = IpAddress.valueOf("0.0.0.0/0");
+        assertTrue(zero.contains(differentSlash32));
+        assertFalse(differentSlash32.contains(zero));
+
+        IpAddress slash8 = IpAddress.valueOf("10.0.0.0/8");
+        assertTrue(slash8.contains(slash31));
+        assertFalse(slash31.contains(slash8));
+    }
 }