Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/apps/config/pom.xml b/apps/config/pom.xml
index 9a2d540..f2a79cb 100644
--- a/apps/config/pom.xml
+++ b/apps/config/pom.xml
@@ -18,18 +18,20 @@
 
     <dependencies>
       <dependency>
-	<groupId>org.codehaus.jackson</groupId>
-	<artifactId>jackson-core-asl</artifactId>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-core-asl</artifactId>
       </dependency>
       <dependency>
-	<groupId>org.codehaus.jackson</groupId>
-	<artifactId>jackson-mapper-asl</artifactId>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-mapper-asl</artifactId>
       </dependency>
       <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-annotations</artifactId>
-        <version>2.4.2</version>
-        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.onlab.onos</groupId>
+        <artifactId>onlab-misc</artifactId>
       </dependency>
     </dependencies> 
 
diff --git a/apps/foo/pom.xml b/apps/foo/pom.xml
index 6109263..f0bff2d 100644
--- a/apps/foo/pom.xml
+++ b/apps/foo/pom.xml
@@ -41,5 +41,17 @@
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.console</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onlab.onos</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
     </dependencies>
 </project>
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
index 08fe0f5..8ead67f 100644
--- a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
+++ b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
@@ -170,10 +170,6 @@
         // We don't yet support bufferids in the flowservice so packet out first.
         packetOut(context, portNumber);
 
-        if (true) {
-            return;
-        }
-
         // Install the flow rule to handle this type of message from now on.
         Ethernet inPkt = context.inPacket().parsed();
         TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
diff --git a/apps/mobility/pom.xml b/apps/mobility/pom.xml
index a919ff2..18b3961 100644
--- a/apps/mobility/pom.xml
+++ b/apps/mobility/pom.xml
@@ -16,4 +16,14 @@
 
     <description>ONOS simple Mobility app</description>
 
+    <dependencies>
+      <dependency>
+        <groupId>com.google.guava</groupId>
+        <artifactId>guava</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>org.onlab.onos</groupId>
+        <artifactId>onlab-misc</artifactId>
+      </dependency>
+    </dependencies>
 </project>
diff --git a/apps/pom.xml b/apps/pom.xml
index 55a786c..eeff7b4 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -23,7 +23,8 @@
         <module>foo</module>
         <module>mobility</module>
         <module>proxyarp</module>
-	    <module>config</module>
+        <module>config</module>
+        <module>sdnip</module>
     </modules>
 
     <properties>
diff --git a/apps/sdnip/pom.xml b/apps/sdnip/pom.xml
new file mode 100644
index 0000000..99960a4
--- /dev/null
+++ b/apps/sdnip/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onlab.onos</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-sdnip</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>SDN-IP peering application</description>
+
+    <dependencies>
+      <dependency>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-core-asl</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-mapper-asl</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>com.fasterxml.jackson.core</groupId>
+        <artifactId>jackson-annotations</artifactId>
+        <version>2.4.2</version>
+        <scope>provided</scope>
+      </dependency>
+    </dependencies> 
+
+</project>
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
new file mode 100644
index 0000000..a4f9b0f
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
@@ -0,0 +1,27 @@
+package org.onlab.onos.sdnip;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.slf4j.Logger;
+
+/**
+ * Placeholder SDN-IP component.
+ */
+@Component(immediate = true)
+public class SdnIp {
+
+    private final Logger log = getLogger(getClass());
+
+    @Activate
+    protected void activate() {
+        log.debug("SDN-IP started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        log.info("Stopped");
+    }
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/package-info.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/package-info.java
new file mode 100644
index 0000000..3e1bcf0
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * SDN-IP peering application.
+ */
+package org.onlab.onos.sdnip;
\ No newline at end of file
diff --git a/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java b/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
index ea6a7cc..42eb4e7 100644
--- a/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
+++ b/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
@@ -141,7 +141,7 @@
     private ObjectNode json(ObjectMapper mapper, ElementId id, int group,
                             String label, boolean isOnline) {
         return mapper.createObjectNode()
-                .put("name", id.uri().toString())
+                .put("name", id.toString())
                 .put("label", label)
                 .put("group", group)
                 .put("online", isOnline);
@@ -202,7 +202,7 @@
     // Returns a formatted string for the element associated with the given
     // connection point.
     private static String id(ConnectPoint cp) {
-        return cp.elementId().uri().toString();
+        return cp.elementId().toString();
     }
 
 }
diff --git a/apps/tvue/src/main/java/org/onlab/onos/tvue/package-info.java b/apps/tvue/src/main/java/org/onlab/onos/tvue/package-info.java
index bfd46a6..c3a6d857 100644
--- a/apps/tvue/src/main/java/org/onlab/onos/tvue/package-info.java
+++ b/apps/tvue/src/main/java/org/onlab/onos/tvue/package-info.java
@@ -1,4 +1,4 @@
 /**
- * REST resources for the sample topology viewer application.
+ * Sample topology viewer application.
  */
 package org.onlab.onos.tvue;
diff --git a/cli/src/main/java/org/onlab/onos/cli/Comparators.java b/cli/src/main/java/org/onlab/onos/cli/Comparators.java
index 98ac624..d44c49f 100644
--- a/cli/src/main/java/org/onlab/onos/cli/Comparators.java
+++ b/cli/src/main/java/org/onlab/onos/cli/Comparators.java
@@ -21,14 +21,14 @@
     public static final Comparator<ElementId> ELEMENT_ID_COMPARATOR = new Comparator<ElementId>() {
         @Override
         public int compare(ElementId id1, ElementId id2) {
-            return id1.uri().toString().compareTo(id2.uri().toString());
+            return id1.toString().compareTo(id2.toString());
         }
     };
 
     public static final Comparator<Element> ELEMENT_COMPARATOR = new Comparator<Element>() {
         @Override
         public int compare(Element e1, Element e2) {
-            return e1.id().uri().toString().compareTo(e2.id().uri().toString());
+            return e1.id().toString().compareTo(e2.id().toString());
         }
     };
 
diff --git a/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java b/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java
index c23d59e..2da76fd 100644
--- a/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java
@@ -1,12 +1,11 @@
 package org.onlab.onos.cli;
 
 import com.google.common.collect.Lists;
-
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cluster.ClusterService;
 import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.MastershipService;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipService;
 
 import java.util.Collections;
 import java.util.List;
@@ -23,7 +22,7 @@
     @Override
     protected void execute() {
         ClusterService service = get(ClusterService.class);
-        DeviceMastershipService mastershipService = get(DeviceMastershipService.class);
+        MastershipService mastershipService = get(MastershipService.class);
         List<ControllerNode> nodes = newArrayList(service.getNodes());
         Collections.sort(nodes, Comparators.NODE_COMPARATOR);
         ControllerNode self = service.getLocalNode();
diff --git a/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java b/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
index e843770..1597b55 100644
--- a/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
@@ -22,8 +22,10 @@
     protected void execute() {
         TopologyService topologyService = get(TopologyService.class);
         Topology topology = topologyService.currentTopology();
-        print("version=%s, nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
-              get(CoreService.class).version().toString(),
+        print("node=%s, version=%s",
+              get(ClusterService.class).getLocalNode().ip(),
+              get(CoreService.class).version().toString());
+        print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
               get(ClusterService.class).getNodes().size(),
               get(DeviceService.class).getDeviceCount(),
               get(LinkService.class).getLinkCount(),
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/DeviceRoleCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/DeviceRoleCommand.java
index 3418aa0..dfb8283 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/DeviceRoleCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/DeviceRoleCommand.java
@@ -3,9 +3,9 @@
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.cluster.MastershipAdminService;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.device.DeviceMastershipAdminService;
 
 import static org.onlab.onos.net.DeviceId.deviceId;
 
@@ -30,7 +30,7 @@
 
     @Override
     protected void execute() {
-        DeviceMastershipAdminService service = get(DeviceMastershipAdminService.class);
+        MastershipAdminService service = get(MastershipAdminService.class);
         MastershipRole mastershipRole = MastershipRole.valueOf(role.toUpperCase());
         service.setRole(new NodeId(node), deviceId(uri), mastershipRole);
     }
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipAdminService.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipAdminService.java
similarity index 78%
rename from core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipAdminService.java
rename to core/api/src/main/java/org/onlab/onos/cluster/MastershipAdminService.java
index 78c9c39..907b3f8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipAdminService.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipAdminService.java
@@ -1,13 +1,12 @@
-package org.onlab.onos.net.device;
+package org.onlab.onos.cluster;
 
-import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
 
 /**
  * Service for administering the inventory of device masterships.
  */
-public interface DeviceMastershipAdminService {
+public interface MastershipAdminService {
 
     /**
      * Applies the current mastership role for the specified device.
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipEvent.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java
similarity index 78%
rename from core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipEvent.java
rename to core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java
index 1232d9c..15811fb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java
@@ -1,13 +1,12 @@
-package org.onlab.onos.net.device;
+package org.onlab.onos.cluster;
 
-import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.event.AbstractEvent;
 import org.onlab.onos.net.DeviceId;
 
 /**
  * Describes a device mastership event.
  */
-public class DeviceMastershipEvent extends AbstractEvent<DeviceMastershipEvent.Type, DeviceId> {
+public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceId> {
 
     //do we worry about explicitly setting slaves/equals? probably not,
     //to keep it simple
@@ -31,7 +30,7 @@
      * @param device event device subject
      * @param master master ID subject
      */
-    public DeviceMastershipEvent(Type type, DeviceId device, NodeId master) {
+    public MastershipEvent(Type type, DeviceId device, NodeId master) {
         super(type, device);
         this.master = master;
     }
@@ -45,7 +44,7 @@
      * @param master master ID subject
      * @param time   occurrence time
      */
-    public DeviceMastershipEvent(Type type, DeviceId device, NodeId master, long time) {
+    public MastershipEvent(Type type, DeviceId device, NodeId master, long time) {
         super(type, device, time);
         this.master = master;
     }
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/MastershipListener.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipListener.java
new file mode 100644
index 0000000..71d65be
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipListener.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.cluster;
+
+import org.onlab.onos.event.EventListener;
+
+/**
+ * Entity capable of receiving device mastership-related events.
+ */
+public interface MastershipListener extends EventListener<MastershipEvent> {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipService.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
similarity index 88%
rename from core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipService.java
rename to core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
index a44abfd..51b6f6a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipService.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
@@ -1,8 +1,7 @@
-package org.onlab.onos.net.device;
+package org.onlab.onos.cluster;
 
 import java.util.Set;
 
-import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
 
@@ -12,7 +11,7 @@
  * determining mastership, but is not responsible for actually applying it
  * to the devices; this falls on the device service.
  */
-public interface DeviceMastershipService {
+public interface MastershipService {
 
     /**
      * Returns the role of the local node for the specified device, without
@@ -63,20 +62,20 @@
      *
      * @return the MastershipTermService for this mastership manager
      */
-    DeviceMastershipTermService requestTermService();
+    MastershipTermService requestTermService();
 
     /**
      * Adds the specified mastership change listener.
      *
      * @param listener the mastership listener
      */
-    void addListener(DeviceMastershipListener listener);
+    void addListener(MastershipListener listener);
 
     /**
      * Removes the specified mastership change listener.
      *
      * @param listener the mastership listener
      */
-    void removeListener(DeviceMastershipListener listener);
+    void removeListener(MastershipListener listener);
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipStore.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java
similarity index 84%
rename from core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipStore.java
rename to core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java
index ee9b253..dc5603f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipStore.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java
@@ -1,8 +1,7 @@
-package org.onlab.onos.net.device;
+package org.onlab.onos.cluster;
 
 import java.util.Set;
 
-import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
 import org.onlab.onos.store.Store;
@@ -11,7 +10,7 @@
  * Manages inventory of mastership roles for devices, across controller
  * instances; not intended for direct use.
  */
-public interface DeviceMastershipStore extends Store<DeviceMastershipEvent, DeviceMastershipStoreDelegate> {
+public interface MastershipStore extends Store<MastershipEvent, MastershipStoreDelegate> {
 
     // three things to map: NodeId, DeviceId, MastershipRole
 
@@ -55,7 +54,7 @@
      * @param deviceId device identifier
      * @return a mastership event
      */
-    DeviceMastershipEvent setMaster(NodeId nodeId, DeviceId deviceId);
+    MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId);
 
     /**
      * Returns the current master and number of past mastership hand-offs
@@ -64,7 +63,7 @@
      * @param deviceId the device identifier
      * @return the current master's ID and the term value for device, or null
      */
-    DeviceMastershipTerm getTermFor(DeviceId deviceId);
+    MastershipTerm getTermFor(DeviceId deviceId);
 
     /**
      * Sets a controller instance's mastership role to STANDBY for a device.
@@ -75,7 +74,7 @@
      * @param deviceId device to revoke mastership role for
      * @return a mastership event
      */
-    DeviceMastershipEvent setStandby(NodeId nodeId, DeviceId deviceId);
+    MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId);
 
     /**
      * Allows a controller instance to give up its current role for a device.
@@ -86,6 +85,6 @@
      * @param deviceId device to revoke mastership role for
      * @return a mastership event
      */
-    DeviceMastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId);
+    MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId);
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/MastershipStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipStoreDelegate.java
new file mode 100644
index 0000000..b4d923c
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipStoreDelegate.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.cluster;
+
+import org.onlab.onos.store.StoreDelegate;
+
+/**
+ * Mastership store delegate abstraction.
+ */
+public interface MastershipStoreDelegate extends StoreDelegate<MastershipEvent> {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipTerm.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipTerm.java
similarity index 64%
rename from core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipTerm.java
rename to core/api/src/main/java/org/onlab/onos/cluster/MastershipTerm.java
index f05a3d2..71d08f2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipTerm.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipTerm.java
@@ -1,21 +1,19 @@
-package org.onlab.onos.net.device;
+package org.onlab.onos.cluster;
 
 import java.util.Objects;
 
-import org.onlab.onos.cluster.NodeId;
-
-public final class DeviceMastershipTerm {
+public final class MastershipTerm {
 
     private final NodeId master;
     private final int termNumber;
 
-    private DeviceMastershipTerm(NodeId master, int term) {
+    private MastershipTerm(NodeId master, int term) {
         this.master = master;
         this.termNumber = term;
     }
 
-    public static DeviceMastershipTerm of(NodeId master, int term) {
-        return new DeviceMastershipTerm(master, term);
+    public static MastershipTerm of(NodeId master, int term) {
+        return new MastershipTerm(master, term);
     }
 
     public NodeId master() {
@@ -36,8 +34,8 @@
         if (this == other) {
             return true;
         }
-        if (other instanceof DeviceMastershipTerm) {
-            DeviceMastershipTerm that = (DeviceMastershipTerm) other;
+        if (other instanceof MastershipTerm) {
+            MastershipTerm that = (MastershipTerm) other;
             if (!this.master.equals(that.master)) {
                 return false;
             }
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipTermService.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipTermService.java
similarity index 74%
rename from core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipTermService.java
rename to core/api/src/main/java/org/onlab/onos/cluster/MastershipTermService.java
index 7a0c063..b03ed81 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipTermService.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipTermService.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.device;
+package org.onlab.onos.cluster;
 
 import org.onlab.onos.net.DeviceId;
 
@@ -6,7 +6,7 @@
 /**
  * Service to obtain mastership term information.
  */
-public interface DeviceMastershipTermService {
+public interface MastershipTermService {
 
     // TBD: manage/increment per device mastership change
     //      or increment on any change
@@ -16,5 +16,5 @@
      * @param deviceId the identifier of the device
      * @return current master's term.
      */
-    DeviceMastershipTerm getMastershipTerm(DeviceId deviceId);
+    MastershipTerm getMastershipTerm(DeviceId deviceId);
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/DeviceId.java b/core/api/src/main/java/org/onlab/onos/net/DeviceId.java
index ef8c5ab..072ba28 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DeviceId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DeviceId.java
@@ -1,15 +1,32 @@
 package org.onlab.onos.net;
 
 import java.net.URI;
+import java.util.Objects;
 
 /**
  * Immutable representation of a device identity.
  */
 public final class DeviceId extends ElementId {
 
+    /**
+     * Represents either no device, or an unspecified device.
+     */
+    public static final DeviceId NONE = deviceId("none:none");
+
+    private final URI uri;
+    private final String str;
+
     // Public construction is prohibited
     private DeviceId(URI uri) {
-        super(uri);
+        this.uri = uri;
+        this.str = uri.toString();
+    }
+
+
+    // Default constructor for serialization
+    protected DeviceId() {
+        this.uri = null;
+        this.str = null;
     }
 
     /**
@@ -30,4 +47,36 @@
         return deviceId(URI.create(string));
     }
 
+    /**
+     * Returns the backing URI.
+     *
+     * @return backing URI
+     */
+    public URI uri() {
+        return uri;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(str);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DeviceId) {
+            final DeviceId that = (DeviceId) obj;
+            return this.getClass() == that.getClass() &&
+                    Objects.equals(this.str, that.str);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/ElementId.java b/core/api/src/main/java/org/onlab/onos/net/ElementId.java
index 3f9c88d..c179d00 100644
--- a/core/api/src/main/java/org/onlab/onos/net/ElementId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/ElementId.java
@@ -1,62 +1,7 @@
 package org.onlab.onos.net;
 
-import java.net.URI;
-import java.util.Objects;
-
 /**
  * Immutable representation of a network element identity.
  */
 public abstract class ElementId {
-
-    private final URI uri;
-    private final String str;
-
-    // Default constructor for serialization
-    protected ElementId() {
-        this.uri = null;
-        this.str = null;
-    }
-
-    /**
-     * Creates an element identifier using the supplied URI.
-     *
-     * @param uri backing URI
-     */
-    protected ElementId(URI uri) {
-        this.uri = uri;
-        this.str = uri.toString();
-    }
-
-    /**
-     * Returns the backing URI.
-     *
-     * @return backing URI
-     */
-    public URI uri() {
-        return uri;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(str);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj instanceof ElementId) {
-            final ElementId that = (ElementId) obj;
-            return this.getClass() == that.getClass() &&
-                    Objects.equals(this.str, that.str);
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return str;
-    }
-
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostId.java b/core/api/src/main/java/org/onlab/onos/net/HostId.java
index f2c0303..ffe558f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostId.java
@@ -3,44 +3,69 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
-import java.net.URI;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * Immutable representation of a host identity.
  */
 public final class HostId extends ElementId {
 
-    private static final String NIC = "nic";
-
     /**
      * Represents either no host, or an unspecified host; used for creating
      * open ingress/egress edge links.
      */
-    public static final HostId NONE = hostId(NIC + ":none-0");
+    public static final HostId NONE = new HostId(MacAddress.ZERO, VlanId.NONE);
+
+    private static final int MAC_LENGTH = 17;
+    private static final int MIN_ID_LENGTH = 19;
+
+    private final MacAddress mac;
+    private final VlanId vlanId;
 
     // Public construction is prohibited
-    private HostId(URI uri) {
-        super(uri);
+    private HostId(MacAddress mac, VlanId vlanId) {
+        this.mac = mac;
+        this.vlanId = vlanId;
+    }
+
+    // Default constructor for serialization
+    private HostId() {
+        this.mac = null;
+        this.vlanId = null;
     }
 
     /**
-     * Creates a device id using the supplied URI.
+     * Returns the host MAC address.
      *
-     * @param uri device URI
-     * @return host identifier
+     * @return MAC address
      */
-    public static HostId hostId(URI uri) {
-        return new HostId(uri);
+    public MacAddress mac() {
+        return mac;
     }
 
     /**
-     * Creates a device id using the supplied URI string.
+     * Returns the host MAC address.
+     *
+     * @return MAC address
+     */
+    public VlanId vlanId() {
+        return vlanId;
+    }
+
+    /**
+     * Creates a device id using the supplied ID string.
      *
      * @param string device URI string
      * @return host identifier
      */
     public static HostId hostId(String string) {
-        return hostId(URI.create(string));
+        checkArgument(string.length() >= MIN_ID_LENGTH,
+                      "Host ID must be at least %s characters", MIN_ID_LENGTH);
+        MacAddress mac = MacAddress.valueOf(string.substring(0, MAC_LENGTH));
+        VlanId vlanId = VlanId.vlanId(Short.parseShort(string.substring(MAC_LENGTH + 1)));
+        return new HostId(mac, vlanId);
     }
 
     /**
@@ -51,7 +76,7 @@
      * @return host identifier
      */
     public static HostId hostId(MacAddress mac, VlanId vlanId) {
-        return hostId(NIC + ":" + mac + "-" + vlanId);
+        return new HostId(mac, vlanId);
     }
 
     /**
@@ -64,4 +89,26 @@
         return hostId(mac, VlanId.vlanId(VlanId.UNTAGGED));
     }
 
+    public String toString() {
+        return mac + "/" + vlanId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mac, vlanId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof HostId) {
+            final HostId other = (HostId) obj;
+            return Objects.equals(this.mac, other.mac) &&
+                    Objects.equals(this.vlanId, other.vlanId);
+        }
+        return false;
+    }
+
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostLocation.java b/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
index 60c5945..3fc3127 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostLocation.java
@@ -1,11 +1,18 @@
 package org.onlab.onos.net;
 
+import static org.onlab.onos.net.PortNumber.P0;
+
 /**
  * Representation of a network edge location where an end-station host is
  * connected.
  */
 public class HostLocation extends ConnectPoint {
 
+    /**
+     * Represents a no location or an unknown location.
+     */
+    public static final HostLocation NONE = new HostLocation(DeviceId.NONE, P0, 0L);
+
     // Note that time is explicitly excluded from the notion of equality.
     private final long time;
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/NetworkResource.java b/core/api/src/main/java/org/onlab/onos/net/NetworkResource.java
new file mode 100644
index 0000000..55e35ba
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/NetworkResource.java
@@ -0,0 +1,7 @@
+package org.onlab.onos.net;
+
+/**
+ * Representation of a network resource.
+ */
+public interface NetworkResource {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java b/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
index e1dcf9e..3967030 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
@@ -4,6 +4,8 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.SparseAnnotations;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * Default implementation of immutable port description.
  */
@@ -48,6 +50,15 @@
         return isEnabled;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("number", number)
+                .add("isEnabled", isEnabled)
+                .add("annotations", annotations())
+                .toString();
+    }
+
     // default constructor for serialization
     private DefaultPortDescription() {
         this.number = null;
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipListener.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipListener.java
deleted file mode 100644
index 0c7c8d9..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipListener.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.onlab.onos.net.device;
-
-import org.onlab.onos.event.EventListener;
-
-/**
- * Entity capable of receiving device mastership-related events.
- */
-public interface DeviceMastershipListener extends EventListener<DeviceMastershipEvent> {
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipStoreDelegate.java
deleted file mode 100644
index f654d85..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceMastershipStoreDelegate.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.onlab.onos.net.device;
-
-import org.onlab.onos.store.StoreDelegate;
-
-/**
- * DeviceMastership store delegate abstraction.
- */
-public interface DeviceMastershipStoreDelegate extends StoreDelegate<DeviceMastershipEvent> {
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java b/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
index bc6e3e5..2e92dad 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
@@ -1,6 +1,5 @@
 package org.onlab.onos.net.host;
 
-import com.google.common.collect.ImmutableSet;
 import org.onlab.onos.net.AbstractDescription;
 import org.onlab.onos.net.HostLocation;
 import org.onlab.onos.net.SparseAnnotations;
@@ -8,9 +7,6 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
-import java.util.HashSet;
-import java.util.Set;
-
 import static com.google.common.base.MoreObjects.toStringHelper;
 
 /**
@@ -22,7 +18,7 @@
     private final MacAddress mac;
     private final VlanId vlan;
     private final HostLocation location;
-    private final Set<IpPrefix> ips;
+    private final IpPrefix ip;
 
     /**
      * Creates a host description using the supplied information.
@@ -35,7 +31,7 @@
     public DefaultHostDescription(MacAddress mac, VlanId vlan,
                                   HostLocation location,
                                   SparseAnnotations... annotations) {
-        this(mac, vlan, location, new HashSet<IpPrefix>(), annotations);
+        this(mac, vlan, location, null, annotations);
     }
 
     /**
@@ -44,17 +40,17 @@
      * @param mac         host MAC address
      * @param vlan        host VLAN identifier
      * @param location    host location
-     * @param ips         of host IP addresses
+     * @param ip          host IP address
      * @param annotations optional key/value annotations map
      */
     public DefaultHostDescription(MacAddress mac, VlanId vlan,
-                                  HostLocation location, Set<IpPrefix> ips,
+                                  HostLocation location, IpPrefix ip,
                                   SparseAnnotations... annotations) {
         super(annotations);
         this.mac = mac;
         this.vlan = vlan;
         this.location = location;
-        this.ips = new HashSet<>(ips);
+        this.ip = ip;
     }
 
     @Override
@@ -73,8 +69,8 @@
     }
 
     @Override
-    public Set<IpPrefix> ipAddresses() {
-        return ImmutableSet.copyOf(ips);
+    public IpPrefix ipAddress() {
+        return ip;
     }
 
     @Override
@@ -83,7 +79,7 @@
                 .add("mac", mac)
                 .add("vlan", vlan)
                 .add("location", location)
-                .add("ipAddresses", ips)
+                .add("ipAddress", ip)
                 .toString();
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java b/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java
index 27014b6..f45a383 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java
@@ -1,7 +1,5 @@
 package org.onlab.onos.net.host;
 
-import java.util.Set;
-
 import org.onlab.onos.net.Description;
 import org.onlab.onos.net.HostLocation;
 import org.onlab.packet.IpPrefix;
@@ -35,10 +33,10 @@
     HostLocation location();
 
     /**
-     * Returns zero or more IP address(es) associated with this host's MAC.
+     * Returns the IP address associated with this host's MAC.
      *
-     * @return a set of IP addresses.
+     * @return host IP address
      */
-    Set<IpPrefix> ipAddresses();
+    IpPrefix ipAddress();
 
 }
diff --git a/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java b/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
index fff6d15..a5f81c7 100644
--- a/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
@@ -1,7 +1,7 @@
 package org.onlab.onos.store;
 
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 
 //TODO: Consider renaming to DeviceClockProviderService?
 /**
@@ -16,5 +16,5 @@
      * @param deviceId device identifier.
      * @param term mastership term.
      */
-    public void setMastershipTerm(DeviceId deviceId, DeviceMastershipTerm term);
+    public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
 }
diff --git a/core/api/src/test/java/org/onlab/onos/TestApplicationId.java b/core/api/src/test/java/org/onlab/onos/TestApplicationId.java
new file mode 100644
index 0000000..e8c0304
--- /dev/null
+++ b/core/api/src/test/java/org/onlab/onos/TestApplicationId.java
@@ -0,0 +1,31 @@
+package org.onlab.onos;
+
+import java.util.Objects;
+
+/**
+ * Test application ID.
+ */
+public class TestApplicationId implements ApplicationId {
+
+    private final String name;
+    private final short id;
+
+    public TestApplicationId(String name) {
+        this.name = name;
+        this.id = (short) Objects.hash(name);
+    }
+
+    public static ApplicationId create(String name) {
+        return new TestApplicationId(name);
+    }
+
+    @Override
+    public short id() {
+        return id;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+}
diff --git a/core/api/src/test/java/org/onlab/onos/cluster/MastershipServiceAdapter.java b/core/api/src/test/java/org/onlab/onos/cluster/MastershipServiceAdapter.java
index 53fafd3..2e92f5b 100644
--- a/core/api/src/test/java/org/onlab/onos/cluster/MastershipServiceAdapter.java
+++ b/core/api/src/test/java/org/onlab/onos/cluster/MastershipServiceAdapter.java
@@ -2,16 +2,13 @@
 
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.device.DeviceMastershipListener;
-import org.onlab.onos.net.device.DeviceMastershipService;
-import org.onlab.onos.net.device.DeviceMastershipTermService;
 
 import java.util.Set;
 
 /**
  * Test adapter for mastership service.
  */
-public class MastershipServiceAdapter implements DeviceMastershipService {
+public class MastershipServiceAdapter implements MastershipService {
     @Override
     public MastershipRole getLocalRole(DeviceId deviceId) {
         return null;
@@ -37,15 +34,15 @@
     }
 
     @Override
-    public void addListener(DeviceMastershipListener listener) {
+    public void addListener(MastershipListener listener) {
     }
 
     @Override
-    public void removeListener(DeviceMastershipListener listener) {
+    public void removeListener(MastershipListener listener) {
     }
 
     @Override
-    public DeviceMastershipTermService requestTermService() {
+    public MastershipTermService requestTermService() {
         return null;
     }
 }
diff --git a/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java b/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java
index 87faf52..139c695 100644
--- a/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java
+++ b/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java
@@ -3,7 +3,6 @@
 import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 
 import com.google.common.testing.EqualsTester;
 
@@ -12,10 +11,10 @@
     private static final NodeId N1 = new NodeId("foo");
     private static final NodeId N2 = new NodeId("bar");
 
-    private static final DeviceMastershipTerm TERM1 = DeviceMastershipTerm.of(N1, 0);
-    private static final DeviceMastershipTerm TERM2 = DeviceMastershipTerm.of(N2, 1);
-    private static final DeviceMastershipTerm TERM3 = DeviceMastershipTerm.of(N2, 1);
-    private static final DeviceMastershipTerm TERM4 = DeviceMastershipTerm.of(N1, 1);
+    private static final MastershipTerm TERM1 = MastershipTerm.of(N1, 0);
+    private static final MastershipTerm TERM2 = MastershipTerm.of(N2, 1);
+    private static final MastershipTerm TERM3 = MastershipTerm.of(N2, 1);
+    private static final MastershipTerm TERM4 = MastershipTerm.of(N1, 1);
 
     @Test
     public void basics() {
@@ -25,7 +24,7 @@
 
     @Test
     public void testEquality() {
-        new EqualsTester().addEqualityGroup(DeviceMastershipTerm.of(N1, 0), TERM1)
+        new EqualsTester().addEqualityGroup(MastershipTerm.of(N1, 0), TERM1)
         .addEqualityGroup(TERM2, TERM3)
         .addEqualityGroup(TERM4);
     }
diff --git a/core/api/src/test/java/org/onlab/onos/event/AbstractEventAccumulatorTest.java b/core/api/src/test/java/org/onlab/onos/event/AbstractEventAccumulatorTest.java
index 9e561bf..ed18195 100644
--- a/core/api/src/test/java/org/onlab/onos/event/AbstractEventAccumulatorTest.java
+++ b/core/api/src/test/java/org/onlab/onos/event/AbstractEventAccumulatorTest.java
@@ -45,15 +45,18 @@
     public void timeTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
         accumulator.add(new TestEvent(FOO, "a"));
-        delay(40);
+        delay(30);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestEvent(FOO, "b"));
-        delay(40);
+        delay(30);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestEvent(FOO, "c"));
-        delay(40);
+        delay(30);
+        assertTrue("should not have fired yet", accumulator.batch.isEmpty());
+        accumulator.add(new TestEvent(FOO, "d"));
+        delay(30);
         assertFalse("should have fired", accumulator.batch.isEmpty());
-        assertEquals("incorrect batch", "abc", accumulator.batch);
+        assertEquals("incorrect batch", "abcd", accumulator.batch);
     }
 
     @Test
diff --git a/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java b/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
index fd63797..9fb9570 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DefaultEdgeLinkTest.java
@@ -18,8 +18,8 @@
 
     private static final ProviderId PID = new ProviderId("of", "foo");
     private static final DeviceId DID1 = deviceId("of:foo");
-    private static final HostId HID1 = hostId("nic:foobar");
-    private static final HostId HID2 = hostId("nic:barfoo");
+    private static final HostId HID1 = hostId("00:00:00:00:00:01/-1");
+    private static final HostId HID2 = hostId("00:00:00:00:00:01/-1");
     private static final PortNumber P0 = portNumber(0);
     private static final PortNumber P1 = portNumber(1);
 
@@ -35,12 +35,8 @@
         EdgeLink l4 = new DefaultEdgeLink(PID, cp(HID2, P0),
                                           new HostLocation(DID1, P1, 123L), false);
 
-        EdgeLink l5 = new DefaultEdgeLink(PID, cp(HID1, P0),
-                                          new HostLocation(DID1, P1, 123L), false);
-
         new EqualsTester().addEqualityGroup(l1, l2)
                 .addEqualityGroup(l3, l4)
-                .addEqualityGroup(l5)
                 .testEquals();
     }
 
diff --git a/core/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java b/core/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java
index eaee54c..295955e 100644
--- a/core/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java
@@ -8,7 +8,7 @@
 /**
  * Test of the device identifier.
  */
-public class DeviceIdTest extends ElementIdTest {
+public class DeviceIdTest {
 
     @Test
     public void basics() {
diff --git a/core/api/src/test/java/org/onlab/onos/net/ElementIdTest.java b/core/api/src/test/java/org/onlab/onos/net/ElementIdTest.java
deleted file mode 100644
index cf209b3..0000000
--- a/core/api/src/test/java/org/onlab/onos/net/ElementIdTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.onlab.onos.net;
-
-import com.google.common.testing.EqualsTester;
-import org.junit.Test;
-
-import java.net.URI;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Test of the network element identifier.
- */
-public class ElementIdTest {
-
-    private static class FooId extends ElementId {
-        public FooId(URI uri) {
-            super(uri);
-        }
-    }
-
-    public static URI uri(String str) {
-        return URI.create(str);
-    }
-
-    @Test
-    public void basics() {
-        new EqualsTester()
-                .addEqualityGroup(new FooId(uri("of:foo")),
-                                  new FooId(uri("of:foo")))
-                .addEqualityGroup(new FooId(uri("of:bar")))
-                .testEquals();
-        assertEquals("wrong uri", uri("ofcfg:foo"),
-                     new FooId(uri("ofcfg:foo")).uri());
-    }
-
-}
diff --git a/core/api/src/test/java/org/onlab/onos/net/HostIdTest.java b/core/api/src/test/java/org/onlab/onos/net/HostIdTest.java
index 712f6b2..40efeb1 100644
--- a/core/api/src/test/java/org/onlab/onos/net/HostIdTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/HostIdTest.java
@@ -11,20 +11,18 @@
 /**
  * Test for the host identifier.
  */
-public class HostIdTest extends ElementIdTest {
+public class HostIdTest {
 
     private static final MacAddress MAC1 = MacAddress.valueOf("00:11:00:00:00:01");
     private static final MacAddress MAC2 = MacAddress.valueOf("00:22:00:00:00:02");
     private static final VlanId VLAN1 = VlanId.vlanId((short) 11);
     private static final VlanId VLAN2 = VlanId.vlanId((short) 22);
 
-    @Override
     @Test
     public void basics() {
         new EqualsTester()
-                .addEqualityGroup(hostId("nic:00:11:00:00:00:01-11"),
-                                  hostId(MAC1, VLAN1))
-                .addEqualityGroup(hostId(MAC2, VLAN2))
+                .addEqualityGroup(hostId(MAC1, VLAN1), hostId(MAC1, VLAN1))
+                .addEqualityGroup(hostId(MAC2, VLAN2), hostId(MAC2, VLAN2))
                 .testEquals();
     }
 
diff --git a/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java b/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
index ce64e46..379ec7a 100644
--- a/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
@@ -31,7 +31,7 @@
 
     // Short-hand for producing a host id from a string
     public static HostId hid(String id) {
-        return hostId("nic:" + id);
+        return hostId(id);
     }
 
     // Crates a new device with the specified id
diff --git a/core/api/src/test/java/org/onlab/onos/net/PortNumberTest.java b/core/api/src/test/java/org/onlab/onos/net/PortNumberTest.java
index 528ad09..d942a98 100644
--- a/core/api/src/test/java/org/onlab/onos/net/PortNumberTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/PortNumberTest.java
@@ -10,9 +10,8 @@
 /**
  * Test of the port number.
  */
-public class PortNumberTest extends ElementIdTest {
+public class PortNumberTest {
 
-    @Override
     @Test
     public void basics() {
         new EqualsTester()
diff --git a/core/api/src/test/java/org/onlab/onos/net/host/DefualtHostDecriptionTest.java b/core/api/src/test/java/org/onlab/onos/net/host/DefualtHostDecriptionTest.java
index 1b00be7..5ae7c27 100644
--- a/core/api/src/test/java/org/onlab/onos/net/host/DefualtHostDecriptionTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/host/DefualtHostDecriptionTest.java
@@ -1,10 +1,5 @@
 package org.onlab.onos.net.host;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Set;
-
 import org.junit.Test;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.HostLocation;
@@ -13,7 +8,8 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
-import com.google.common.collect.Sets;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Test for the default host description.
@@ -22,24 +18,22 @@
 
     private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
     private static final VlanId VLAN = VlanId.vlanId((short) 10);
+    private static final IpPrefix IP = IpPrefix.valueOf("10.0.0.1");
+
     private static final HostLocation LOC = new HostLocation(
-                DeviceId.deviceId("of:foo"),
-                PortNumber.portNumber(100),
-                123L
-            );
-    private static final Set<IpPrefix> IPS = Sets.newHashSet(
-                IpPrefix.valueOf("10.0.0.1"),
-                IpPrefix.valueOf("10.0.0.2")
-            );
+            DeviceId.deviceId("of:foo"),
+            PortNumber.portNumber(100),
+            123L
+    );
 
     @Test
     public void basics() {
         HostDescription host =
-                new DefaultHostDescription(MAC, VLAN, LOC, IPS);
+                new DefaultHostDescription(MAC, VLAN, LOC, IP);
         assertEquals("incorrect mac", MAC, host.hwAddress());
         assertEquals("incorrect vlan", VLAN, host.vlan());
         assertEquals("incorrect location", LOC, host.location());
-        assertTrue("incorrect ip's", IPS.equals(host.ipAddresses()));
+        assertEquals("incorrect ip's", IP, host.ipAddress());
         assertTrue("incorrect toString", host.toString().contains("vlan=10"));
     }
 
diff --git a/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java b/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java
index b9c6ae8..125745b 100644
--- a/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java
+++ b/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java
@@ -14,25 +14,25 @@
 import org.onlab.onos.cluster.ClusterEvent;
 import org.onlab.onos.cluster.ClusterEventListener;
 import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.MastershipAdminService;
+import org.onlab.onos.cluster.MastershipEvent;
+import org.onlab.onos.cluster.MastershipListener;
+import org.onlab.onos.cluster.MastershipService;
+import org.onlab.onos.cluster.MastershipStore;
+import org.onlab.onos.cluster.MastershipStoreDelegate;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.MastershipTermService;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.event.AbstractListenerRegistry;
 import org.onlab.onos.event.EventDeliveryService;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.device.DeviceMastershipAdminService;
-import org.onlab.onos.net.device.DeviceMastershipEvent;
-import org.onlab.onos.net.device.DeviceMastershipListener;
-import org.onlab.onos.net.device.DeviceMastershipService;
-import org.onlab.onos.net.device.DeviceMastershipStore;
-import org.onlab.onos.net.device.DeviceMastershipStoreDelegate;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
-import org.onlab.onos.net.device.DeviceMastershipTermService;
 import org.slf4j.Logger;
 
 @Component(immediate = true)
 @Service
 public class MastershipManager
-implements DeviceMastershipService, DeviceMastershipAdminService {
+implements MastershipService, MastershipAdminService {
 
     private static final String NODE_ID_NULL = "Node ID cannot be null";
     private static final String DEVICE_ID_NULL = "Device ID cannot be null";
@@ -40,13 +40,13 @@
 
     private final Logger log = getLogger(getClass());
 
-    protected final AbstractListenerRegistry<DeviceMastershipEvent, DeviceMastershipListener>
+    protected final AbstractListenerRegistry<MastershipEvent, MastershipListener>
     listenerRegistry = new AbstractListenerRegistry<>();
 
-    private final DeviceMastershipStoreDelegate delegate = new InternalDelegate();
+    private final MastershipStoreDelegate delegate = new InternalDelegate();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceMastershipStore store;
+    protected MastershipStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
@@ -58,7 +58,7 @@
 
     @Activate
     public void activate() {
-        eventDispatcher.addSink(DeviceMastershipEvent.class, listenerRegistry);
+        eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
         clusterService.addListener(clusterListener);
         store.setDelegate(delegate);
         log.info("Started");
@@ -66,7 +66,7 @@
 
     @Deactivate
     public void deactivate() {
-        eventDispatcher.removeSink(DeviceMastershipEvent.class);
+        eventDispatcher.removeSink(MastershipEvent.class);
         clusterService.removeListener(clusterListener);
         store.unsetDelegate(delegate);
         log.info("Stopped");
@@ -78,7 +78,7 @@
         checkNotNull(deviceId, DEVICE_ID_NULL);
         checkNotNull(role, ROLE_NULL);
 
-        DeviceMastershipEvent event = null;
+        MastershipEvent event = null;
         if (role.equals(MastershipRole.MASTER)) {
             event = store.setMaster(nodeId, deviceId);
         } else {
@@ -98,7 +98,7 @@
 
     @Override
     public void relinquishMastership(DeviceId deviceId) {
-        DeviceMastershipEvent event = null;
+        MastershipEvent event = null;
         event = store.relinquishRole(
                 clusterService.getLocalNode().id(), deviceId);
 
@@ -127,18 +127,18 @@
 
 
     @Override
-    public DeviceMastershipTermService requestTermService() {
+    public MastershipTermService requestTermService() {
         return new InternalMastershipTermService();
     }
 
     @Override
-    public void addListener(DeviceMastershipListener listener) {
+    public void addListener(MastershipListener listener) {
         checkNotNull(listener);
         listenerRegistry.addListener(listener);
     }
 
     @Override
-    public void removeListener(DeviceMastershipListener listener) {
+    public void removeListener(MastershipListener listener) {
         checkNotNull(listener);
         listenerRegistry.removeListener(listener);
     }
@@ -146,16 +146,16 @@
     // FIXME: provide wiring to allow events to be triggered by changes within the store
 
     // Posts the specified event to the local event dispatcher.
-    private void post(DeviceMastershipEvent event) {
+    private void post(MastershipEvent event) {
         if (event != null && eventDispatcher != null) {
             eventDispatcher.post(event);
         }
     }
 
-    private class InternalMastershipTermService implements DeviceMastershipTermService {
+    private class InternalMastershipTermService implements MastershipTermService {
 
         @Override
-        public DeviceMastershipTerm getMastershipTerm(DeviceId deviceId) {
+        public MastershipTerm getMastershipTerm(DeviceId deviceId) {
             return store.getTermFor(deviceId);
         }
 
@@ -181,10 +181,10 @@
 
     }
 
-    public class InternalDelegate implements DeviceMastershipStoreDelegate {
+    public class InternalDelegate implements MastershipStoreDelegate {
 
         @Override
-        public void notify(DeviceMastershipEvent event) {
+        public void notify(MastershipEvent event) {
             log.info("dispatching mastership event {}", event);
             eventDispatcher.post(event);
         }
diff --git a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
index d4b8889..8cde5a3 100644
--- a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
@@ -13,6 +13,11 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.MastershipEvent;
+import org.onlab.onos.cluster.MastershipListener;
+import org.onlab.onos.cluster.MastershipService;
+import org.onlab.onos.cluster.MastershipTermService;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.event.AbstractListenerRegistry;
 import org.onlab.onos.event.EventDeliveryService;
@@ -31,11 +36,6 @@
 import org.onlab.onos.net.device.DeviceService;
 import org.onlab.onos.net.device.DeviceStore;
 import org.onlab.onos.net.device.DeviceStoreDelegate;
-import org.onlab.onos.net.device.DeviceMastershipEvent;
-import org.onlab.onos.net.device.DeviceMastershipListener;
-import org.onlab.onos.net.device.DeviceMastershipService;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
-import org.onlab.onos.net.device.DeviceMastershipTermService;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.AbstractProviderRegistry;
 import org.onlab.onos.net.provider.AbstractProviderService;
@@ -64,7 +64,7 @@
 
     private final DeviceStoreDelegate delegate = new InternalStoreDelegate();
 
-    private final DeviceMastershipListener mastershipListener = new InternalMastershipListener();
+    private final MastershipListener mastershipListener = new InternalMastershipListener();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceStore store;
@@ -76,9 +76,9 @@
     protected ClusterService clusterService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceMastershipService mastershipService;
+    protected MastershipService mastershipService;
 
-    protected DeviceMastershipTermService termService;
+    protected MastershipTermService termService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ClockProviderService clockProviderService;
@@ -209,7 +209,7 @@
                 return;
             }
 
-            DeviceMastershipTerm term = mastershipService.requestTermService()
+            MastershipTerm term = mastershipService.requestTermService()
                     .getMastershipTerm(deviceId);
             if (!term.master().equals(clusterService.getLocalNode().id())) {
                 // lost mastership after requestRole told this instance was MASTER.
@@ -320,16 +320,16 @@
     }
 
     // Intercepts mastership events
-    private class InternalMastershipListener implements DeviceMastershipListener {
+    private class InternalMastershipListener implements MastershipListener {
 
         @Override
-        public void event(DeviceMastershipEvent event) {
+        public void event(MastershipEvent event) {
             final DeviceId did = event.subject();
             if (isAvailable(did)) {
                 final NodeId myNodeId = clusterService.getLocalNode().id();
 
                 if (myNodeId.equals(event.master())) {
-                    DeviceMastershipTerm term = termService.getMastershipTerm(did);
+                    MastershipTerm term = termService.getMastershipTerm(did);
 
                     if (term.master().equals(myNodeId)) {
                         // only set the new term if I am the master
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
index 29a0f18..4f9bcbb 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java
@@ -168,7 +168,6 @@
         checkNotNull(hostId, HOST_ID_NULL);
         HostEvent event = store.removeHost(hostId);
         if (event != null) {
-            log.info("Host {} administratively removed", hostId);
             post(event);
         }
     }
@@ -214,7 +213,6 @@
             HostEvent event = store.createOrUpdateHost(provider().id(), hostId,
                                                        hostDescription);
             if (event != null) {
-                log.debug("Host {} detected", hostId);
                 post(event);
             }
         }
@@ -225,7 +223,6 @@
             checkValidity();
             HostEvent event = store.removeHost(hostId);
             if (event != null) {
-                log.debug("Host {} vanished", hostId);
                 post(event);
             }
         }
diff --git a/core/net/src/main/java/org/onlab/onos/net/topology/impl/PathManager.java b/core/net/src/main/java/org/onlab/onos/net/topology/impl/PathManager.java
index 86be9a5..849ac6d 100644
--- a/core/net/src/main/java/org/onlab/onos/net/topology/impl/PathManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/topology/impl/PathManager.java
@@ -22,9 +22,9 @@
 import org.onlab.onos.net.Path;
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.host.HostService;
-import org.onlab.onos.net.topology.PathService;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.onos.net.topology.LinkWeight;
+import org.onlab.onos.net.topology.PathService;
 import org.onlab.onos.net.topology.Topology;
 import org.onlab.onos.net.topology.TopologyService;
 import org.slf4j.Logger;
@@ -33,7 +33,6 @@
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onlab.onos.net.DeviceId.deviceId;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -162,8 +161,8 @@
     // edge link since the src or dst are really an infrastructure device.
     private static class NotHost extends DefaultEdgeLink implements EdgeLink {
         NotHost() {
-            super(PID, new ConnectPoint(HostId.hostId("nic:none"), P0),
-                  new HostLocation(deviceId("none:none"), P0, 0L), false);
+            super(PID, new ConnectPoint(HostId.NONE, P0),
+                  new HostLocation(DeviceId.NONE, P0, 0L), false);
         }
     }
 }
diff --git a/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java b/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java
index 84ca891..29b4ddf 100644
--- a/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java
@@ -10,11 +10,11 @@
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.ControllerNode.State;
 import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipService;
+import org.onlab.onos.cluster.MastershipTermService;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.event.impl.TestEventDispatcher;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipService;
-import org.onlab.onos.net.device.DeviceMastershipTermService;
 import org.onlab.onos.store.trivial.impl.SimpleMastershipStore;
 import org.onlab.packet.IpPrefix;
 
@@ -34,7 +34,7 @@
     private static final DeviceId DEV_OTHER = DeviceId.deviceId("of:2");
 
     private MastershipManager mgr;
-    protected DeviceMastershipService service;
+    protected MastershipService service;
 
     @Before
     public void setUp() {
@@ -120,7 +120,7 @@
 
     @Test
     public void termService() {
-        DeviceMastershipTermService ts = mgr.requestTermService();
+        MastershipTermService ts = mgr.requestTermService();
 
         //term = 0 for both
         mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
diff --git a/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
index bc4e353..e833b4a 100644
--- a/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
@@ -11,6 +11,8 @@
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.DefaultControllerNode;
 import org.onlab.onos.cluster.MastershipServiceAdapter;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.MastershipTermService;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.cluster.ControllerNode.State;
 import org.onlab.onos.event.Event;
@@ -30,8 +32,6 @@
 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.DeviceMastershipTerm;
-import org.onlab.onos.net.device.DeviceMastershipTermService;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.AbstractProvider;
 import org.onlab.onos.net.provider.ProviderId;
@@ -290,12 +290,12 @@
         }
 
         @Override
-        public DeviceMastershipTermService requestTermService() {
-            return new DeviceMastershipTermService() {
+        public MastershipTermService requestTermService() {
+            return new MastershipTermService() {
                 @Override
-                public DeviceMastershipTerm getMastershipTerm(DeviceId deviceId) {
+                public MastershipTerm getMastershipTerm(DeviceId deviceId) {
                     // FIXME: just returning something not null
-                    return DeviceMastershipTerm.of(NID_LOCAL, 1);
+                    return MastershipTerm.of(NID_LOCAL, 1);
                 }
             };
         }
@@ -339,7 +339,7 @@
             ClockProviderService {
 
         @Override
-        public void setMastershipTerm(DeviceId deviceId, DeviceMastershipTerm term) {
+        public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
             // TODO Auto-generated method stub
         }
     }
diff --git a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
index 0b07380..6864fd7 100644
--- a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
@@ -58,8 +58,6 @@
 
     private static final IpPrefix IP1 = IpPrefix.valueOf("10.0.0.1");
     private static final IpPrefix IP2 = IpPrefix.valueOf("10.0.0.2");
-    private static final Set<IpPrefix> IPSET1 = Sets.newHashSet(IP1);
-    private static final Set<IpPrefix> IPSET2 = Sets.newHashSet(IP2);
 
     private static final DeviceId DID1 = DeviceId.deviceId("of:001");
     private static final DeviceId DID2 = DeviceId.deviceId("of:002");
@@ -94,14 +92,14 @@
         provider = new TestHostProvider();
         providerService = registry.register(provider);
         assertTrue("provider should be registered",
-                registry.getProviders().contains(provider.id()));
+                   registry.getProviders().contains(provider.id()));
     }
 
     @After
     public void tearDown() {
         registry.unregister(provider);
         assertFalse("provider should not be registered",
-                registry.getProviders().contains(provider.id()));
+                    registry.getProviders().contains(provider.id()));
 
         mgr.removeListener(listener);
         mgr.deactivate();
@@ -109,8 +107,8 @@
     }
 
     private void detect(HostId hid, MacAddress mac, VlanId vlan,
-            HostLocation loc, Set<IpPrefix> ips) {
-        HostDescription descr = new DefaultHostDescription(mac, vlan, loc, ips);
+                        HostLocation loc, IpPrefix ip) {
+        HostDescription descr = new DefaultHostDescription(mac, vlan, loc, ip);
         providerService.hostDetected(hid, descr);
         assertNotNull("host should be found", mgr.getHost(hid));
     }
@@ -130,26 +128,26 @@
         assertNull("host shouldn't be found", mgr.getHost(HID1));
 
         // host addition
-        detect(HID1, MAC1, VLAN1, LOC1, IPSET1);
+        detect(HID1, MAC1, VLAN1, LOC1, IP1);
         assertEquals("exactly one should be found", 1, mgr.getHostCount());
-        detect(HID2, MAC2, VLAN2, LOC2, IPSET1);
+        detect(HID2, MAC2, VLAN2, LOC2, IP1);
         assertEquals("two hosts should be found", 2, mgr.getHostCount());
         validateEvents(HOST_ADDED, HOST_ADDED);
 
         // host motion
-        detect(HID1, MAC1, VLAN1, LOC2, IPSET1);
+        detect(HID1, MAC1, VLAN1, LOC2, IP1);
         validateEvents(HOST_MOVED);
         assertEquals("only two hosts should be found", 2, mgr.getHostCount());
 
         // host update
-        detect(HID1, MAC1, VLAN1, LOC2, IPSET2);
+        detect(HID1, MAC1, VLAN1, LOC2, IP2);
         validateEvents(HOST_UPDATED);
         assertEquals("only two hosts should be found", 2, mgr.getHostCount());
     }
 
     @Test
     public void hostVanished() {
-        detect(HID1, MAC1, VLAN1, LOC1, IPSET1);
+        detect(HID1, MAC1, VLAN1, LOC1, IP1);
         providerService.hostVanished(HID1);
         validateEvents(HOST_ADDED, HOST_REMOVED);
 
@@ -157,7 +155,7 @@
     }
 
     private void validateHosts(
-            String msg, Iterable<Host> hosts, HostId ... ids) {
+            String msg, Iterable<Host> hosts, HostId... ids) {
         Set<HostId> hids = Sets.newHashSet(ids);
         for (Host h : hosts) {
             assertTrue(msg, hids.remove(h.id()));
@@ -167,8 +165,8 @@
 
     @Test
     public void getHosts() {
-        detect(HID1, MAC1, VLAN1, LOC1, IPSET1);
-        detect(HID2, MAC2, VLAN1, LOC2, IPSET2);
+        detect(HID1, MAC1, VLAN1, LOC1, IP1);
+        detect(HID2, MAC2, VLAN1, LOC2, IP2);
 
         validateHosts("host not properly stored", mgr.getHosts(), HID1, HID2);
         validateHosts("can't get hosts by VLAN", mgr.getHostsByVlan(VLAN1), HID1, HID2);
@@ -210,7 +208,7 @@
     @Test
     public void bindAddressesToPort() {
         PortAddresses add1 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
+                                               Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
 
         mgr.bindAddressesToPort(add1);
         PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -241,7 +239,7 @@
     @Test
     public void unbindAddressesFromPort() {
         PortAddresses add1 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
+                                               Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
 
         mgr.bindAddressesToPort(add1);
         PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -250,7 +248,7 @@
         assertNotNull(storedAddresses.mac());
 
         PortAddresses rem1 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX1), null);
+                                               Sets.newHashSet(PREFIX1), null);
 
         mgr.unbindAddressesFromPort(rem1);
         storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -267,7 +265,7 @@
         assertNull(storedAddresses.mac());
 
         PortAddresses rem3 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX2), MAC1);
+                                               Sets.newHashSet(PREFIX2), MAC1);
 
         mgr.unbindAddressesFromPort(rem3);
         storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -279,7 +277,7 @@
     @Test
     public void clearAddresses() {
         PortAddresses add1 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
+                                               Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
 
         mgr.bindAddressesToPort(add1);
         PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -297,7 +295,7 @@
     @Test
     public void getAddressBindingsForPort() {
         PortAddresses add1 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
+                                               Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
 
         mgr.bindAddressesToPort(add1);
         PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -314,7 +312,7 @@
         assertTrue(storedAddresses.isEmpty());
 
         PortAddresses add1 = new PortAddresses(CP1,
-                Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
+                                               Sets.newHashSet(PREFIX1, PREFIX2), MAC1);
 
         mgr.bindAddressesToPort(add1);
 
@@ -323,7 +321,7 @@
         assertTrue(storedAddresses.size() == 1);
 
         PortAddresses add2 = new PortAddresses(CP2,
-                Sets.newHashSet(PREFIX3), MAC2);
+                                               Sets.newHashSet(PREFIX3), MAC2);
 
         mgr.bindAddressesToPort(add2);
 
diff --git a/core/net/src/test/java/org/onlab/onos/net/topology/impl/PathManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/topology/impl/PathManagerTest.java
index 2ecf7f5..c22f985 100644
--- a/core/net/src/test/java/org/onlab/onos/net/topology/impl/PathManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/topology/impl/PathManagerTest.java
@@ -65,47 +65,48 @@
     @Test
     public void infraToEdge() {
         DeviceId src = did("src");
-        HostId dst = hid("dst");
+        HostId dst = hid("12:34:56:78:90:ab/1");
         fakeTopoMgr.paths.add(createPath("src", "middle", "edge"));
-        fakeHostMgr.hosts.put(dst, host("dst", "edge"));
+        fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ab/1", "edge"));
         Set<Path> paths = service.getPaths(src, dst);
         validatePaths(paths, 1, 3, src, dst);
     }
 
     @Test
     public void edgeToInfra() {
-        HostId src = hid("src");
+        HostId src = hid("12:34:56:78:90:ab/1");
         DeviceId dst = did("dst");
         fakeTopoMgr.paths.add(createPath("edge", "middle", "dst"));
-        fakeHostMgr.hosts.put(src, host("src", "edge"));
+        fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "edge"));
         Set<Path> paths = service.getPaths(src, dst);
         validatePaths(paths, 1, 3, src, dst);
     }
 
     @Test
     public void edgeToEdge() {
-        HostId src = hid("src");
-        HostId dst = hid("dst");
+        HostId src = hid("12:34:56:78:90:ab/1");
+        HostId dst = hid("12:34:56:78:90:ef/1");
         fakeTopoMgr.paths.add(createPath("srcEdge", "middle", "dstEdge"));
-        fakeHostMgr.hosts.put(src, host("src", "srcEdge"));
-        fakeHostMgr.hosts.put(dst, host("dst", "dstEdge"));
+        fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "srcEdge"));
+        fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ef/1", "dstEdge"));
         Set<Path> paths = service.getPaths(src, dst);
         validatePaths(paths, 1, 4, src, dst);
     }
 
     @Test
     public void edgeToEdgeDirect() {
-        HostId src = hid("src");
-        HostId dst = hid("dst");
-        fakeHostMgr.hosts.put(src, host("src", "edge"));
-        fakeHostMgr.hosts.put(dst, host("dst", "edge"));
+        HostId src = hid("12:34:56:78:90:ab/1");
+        HostId dst = hid("12:34:56:78:90:ef/1");
+        fakeHostMgr.hosts.put(src, host("12:34:56:78:90:ab/1", "edge"));
+        fakeHostMgr.hosts.put(dst, host("12:34:56:78:90:ef/1", "edge"));
         Set<Path> paths = service.getPaths(src, dst);
         validatePaths(paths, 1, 2, src, dst);
     }
 
     @Test
     public void noEdge() {
-        Set<Path> paths = service.getPaths(hid("src"), hid("dst"));
+        Set<Path> paths = service.getPaths(hid("12:34:56:78:90:ab/1"),
+                                           hid("12:34:56:78:90:ef/1"));
         assertTrue("there should be no paths", paths.isEmpty());
     }
 
diff --git a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
index 15b7eca..d369073 100644
--- a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
@@ -134,11 +134,11 @@
                     service.isInfrastructure(topology, new ConnectPoint(did("a"), portNumber(3))));
 
         // One of these cannot be a broadcast point... or we have a loop...
-        assertFalse("should not be broadcast point",
-                    service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(1))) &&
-                            service.isBroadcastPoint(topology, new ConnectPoint(did("b"), portNumber(1))) &&
-                            service.isBroadcastPoint(topology, new ConnectPoint(did("c"), portNumber(1))) &&
-                            service.isBroadcastPoint(topology, new ConnectPoint(did("d"), portNumber(1))));
+//        assertFalse("should not be broadcast point",
+//                    service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(1))) &&
+//                            service.isBroadcastPoint(topology, new ConnectPoint(did("b"), portNumber(1))) &&
+//                            service.isBroadcastPoint(topology, new ConnectPoint(did("c"), portNumber(1))) &&
+//                            service.isBroadcastPoint(topology, new ConnectPoint(did("d"), portNumber(1))));
         assertTrue("should be broadcast point",
                    service.isBroadcastPoint(topology, new ConnectPoint(did("a"), portNumber(3))));
     }
diff --git a/core/store/dist/pom.xml b/core/store/dist/pom.xml
index 1faab74..7b00833 100644
--- a/core/store/dist/pom.xml
+++ b/core/store/dist/pom.xml
@@ -54,8 +54,13 @@
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
         <dependency>
-          <groupId>de.javakaffee</groupId>
-          <artifactId>kryo-serializers</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava-testlib</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
         </dependency>
     </dependencies>
 
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
index c72fae8..1b11873 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
@@ -6,7 +6,6 @@
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -24,8 +23,10 @@
 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
 import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.serializers.ClusterMessageSerializer;
 import org.onlab.onos.store.serializers.KryoPoolUtil;
 import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.serializers.MessageSubjectSerializer;
 import org.onlab.util.KryoPool;
 import org.onlab.netty.Endpoint;
 import org.onlab.netty.Message;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyAdvertisement.java
deleted file mode 100644
index 132f27a..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyAdvertisement.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.onlab.onos.store.common.impl;
-
-import java.util.Map;
-
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.store.Timestamp;
-
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Anti-Entropy advertisement message.
- * <p>
- * Message to advertise the information this node holds.
- *
- * @param <ID> ID type
- */
-public class AntiEntropyAdvertisement<ID> {
-
-    private final NodeId sender;
-    private final ImmutableMap<ID, Timestamp> advertisement;
-
-    /**
-     * Creates anti-entropy advertisement message.
-     *
-     * @param sender sender of this message
-     * @param advertisement timestamp information of the data sender holds
-     */
-    public AntiEntropyAdvertisement(NodeId sender, Map<ID, Timestamp> advertisement) {
-        this.sender = sender;
-        this.advertisement = ImmutableMap.copyOf(advertisement);
-    }
-
-    public NodeId sender() {
-        return sender;
-    }
-
-    public ImmutableMap<ID, Timestamp> advertisement() {
-        return advertisement;
-    }
-
-    // Default constructor for serializer
-    protected AntiEntropyAdvertisement() {
-        this.sender = null;
-        this.advertisement = null;
-    }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyReply.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyReply.java
deleted file mode 100644
index 46a2333..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/AntiEntropyReply.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.onlab.onos.store.common.impl;
-
-import java.util.Map;
-import java.util.Set;
-
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.store.VersionedValue;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-/**
- * Anti-Entropy reply message.
- * <p>
- * Message to send in reply to advertisement or another reply.
- * Suggest to the sender about the more up-to-date data this node has,
- * and request for more recent data that the receiver has.
- */
-public class AntiEntropyReply<ID, V extends VersionedValue<?>> {
-
-    private final NodeId sender;
-    private final ImmutableMap<ID, V> suggestion;
-    private final ImmutableSet<ID> request;
-
-    /**
-     * Creates a reply to anti-entropy message.
-     *
-     * @param sender sender of this message
-     * @param suggestion collection of more recent values, sender had
-     * @param request Collection of identifiers
-     */
-    public AntiEntropyReply(NodeId sender,
-                            Map<ID, V> suggestion,
-                            Set<ID> request) {
-        this.sender = sender;
-        this.suggestion = ImmutableMap.copyOf(suggestion);
-        this.request = ImmutableSet.copyOf(request);
-    }
-
-    public NodeId sender() {
-        return sender;
-    }
-
-    /**
-     * Returns collection of values, which the recipient of this reply is likely
-     * to be missing or has outdated version.
-     *
-     * @return
-     */
-    public ImmutableMap<ID, V> suggestion() {
-        return suggestion;
-    }
-
-    /**
-     * Returns collection of identifier to request.
-     *
-     * @return collection of identifier to request
-     */
-    public ImmutableSet<ID> request() {
-        return request;
-    }
-
-    /**
-     * Checks if reply contains any suggestion or request.
-     *
-     * @return true if nothing is suggested and requested
-     */
-    public boolean isEmpty() {
-        return suggestion.isEmpty() && request.isEmpty();
-    }
-
-    // Default constructor for serializer
-    protected AntiEntropyReply() {
-        this.sender = null;
-        this.suggestion = null;
-        this.request = null;
-    }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/ControllerNodeToNodeId.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/ControllerNodeToNodeId.java
new file mode 100644
index 0000000..73187ee
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/ControllerNodeToNodeId.java
@@ -0,0 +1,24 @@
+package org.onlab.onos.store.common.impl;
+
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.NodeId;
+
+import com.google.common.base.Function;
+
+/**
+ * Function to convert ControllerNode to NodeId.
+ */
+public final class ControllerNodeToNodeId
+    implements Function<ControllerNode, NodeId> {
+
+    private static final ControllerNodeToNodeId INSTANCE = new ControllerNodeToNodeId();
+
+    @Override
+    public NodeId apply(ControllerNode input) {
+        return input.id();
+    }
+
+    public static ControllerNodeToNodeId toNodeId() {
+        return INSTANCE;
+    }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/DeviceMastershipBasedTimestamp.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
similarity index 80%
rename from core/store/dist/src/main/java/org/onlab/onos/store/common/impl/DeviceMastershipBasedTimestamp.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
index 4bc41ab..0f4f894 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/DeviceMastershipBasedTimestamp.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
@@ -13,7 +13,7 @@
  * Default implementation of Timestamp.
  * TODO: Better documentation.
  */
-public final class DeviceMastershipBasedTimestamp implements Timestamp {
+public final class MastershipBasedTimestamp implements Timestamp {
 
     private final int termNumber;
     private final int sequenceNumber;
@@ -24,16 +24,16 @@
      * @param termNumber the mastership termNumber
      * @param sequenceNumber  the sequenceNumber number within the termNumber
      */
-    public DeviceMastershipBasedTimestamp(int termNumber, int sequenceNumber) {
+    public MastershipBasedTimestamp(int termNumber, int sequenceNumber) {
         this.termNumber = termNumber;
         this.sequenceNumber = sequenceNumber;
     }
 
     @Override
     public int compareTo(Timestamp o) {
-        checkArgument(o instanceof DeviceMastershipBasedTimestamp,
+        checkArgument(o instanceof MastershipBasedTimestamp,
                 "Must be MastershipBasedTimestamp", o);
-        DeviceMastershipBasedTimestamp that = (DeviceMastershipBasedTimestamp) o;
+        MastershipBasedTimestamp that = (MastershipBasedTimestamp) o;
 
         return ComparisonChain.start()
                 .compare(this.termNumber, that.termNumber)
@@ -51,10 +51,10 @@
         if (this == obj) {
             return true;
         }
-        if (!(obj instanceof DeviceMastershipBasedTimestamp)) {
+        if (!(obj instanceof MastershipBasedTimestamp)) {
             return false;
         }
-        DeviceMastershipBasedTimestamp that = (DeviceMastershipBasedTimestamp) obj;
+        MastershipBasedTimestamp that = (MastershipBasedTimestamp) obj;
         return Objects.equals(this.termNumber, that.termNumber) &&
                 Objects.equals(this.sequenceNumber, that.sequenceNumber);
     }
@@ -87,7 +87,7 @@
 
     // Default constructor for serialization
     @Deprecated
-    protected DeviceMastershipBasedTimestamp() {
+    protected MastershipBasedTimestamp() {
         this.termNumber = -1;
         this.sequenceNumber = -1;
     }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampSerializer.java
deleted file mode 100644
index 99b4f95..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampSerializer.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.onlab.onos.store.common.impl;
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-
-// To be used if Timestamp ever needs to cross bundle boundary.
-/**
- * Kryo Serializer for {@link DeviceMastershipBasedTimestamp}.
- */
-public class MastershipBasedTimestampSerializer extends Serializer<DeviceMastershipBasedTimestamp> {
-
-    /**
-     * Creates a serializer for {@link DeviceMastershipBasedTimestamp}.
-     */
-    public MastershipBasedTimestampSerializer() {
-        // non-null, immutable
-        super(false, true);
-    }
-
-    @Override
-    public void write(Kryo kryo, Output output, DeviceMastershipBasedTimestamp object) {
-        output.writeInt(object.termNumber());
-        output.writeInt(object.sequenceNumber());
-    }
-
-    @Override
-    public DeviceMastershipBasedTimestamp read(Kryo kryo, Input input, Class<DeviceMastershipBasedTimestamp> type) {
-        final int term = input.readInt();
-        final int sequence = input.readInt();
-        return new DeviceMastershipBasedTimestamp(term, sequence);
-    }
-}
diff --git a/core/api/src/main/java/org/onlab/onos/store/Timestamped.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
similarity index 80%
rename from core/api/src/main/java/org/onlab/onos/store/Timestamped.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
index 9de7b0d..e803e74 100644
--- a/core/api/src/main/java/org/onlab/onos/store/Timestamped.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
@@ -1,9 +1,11 @@
-package org.onlab.onos.store;
+package org.onlab.onos.store.common.impl;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Objects;
 
+import org.onlab.onos.store.Timestamp;
+
 import com.google.common.base.MoreObjects;
 
 /**
@@ -28,6 +30,7 @@
 
     /**
      * Returns the value.
+     *
      * @return value
      */
     public T value() {
@@ -36,6 +39,7 @@
 
     /**
      * Returns the time stamp.
+     *
      * @return time stamp
      */
     public Timestamp timestamp() {
@@ -49,7 +53,16 @@
      * @return true if this instance is newer.
      */
     public boolean isNewer(Timestamped<T> other) {
-        return this.timestamp.compareTo(checkNotNull(other).timestamp()) > 0;
+        return isNewer(checkNotNull(other).timestamp());
+    }
+
+    /**
+     * Tests if this timestamp is newer thatn the specified timestamp.
+     * @param timestamp to compare agains
+     * @return true if this instance is newer
+     */
+    public boolean isNewer(Timestamp timestamp) {
+        return this.timestamp.compareTo(checkNotNull(timestamp)) > 0;
     }
 
     @Override
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
deleted file mode 100644
index c9f232a..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.onlab.onos.store.device.impl;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.VersionedValue;
-import org.onlab.onos.store.common.impl.AntiEntropyAdvertisement;
-
-// TODO DeviceID needs to be changed to something like (ProviderID, DeviceID)
-// TODO: Handle Port as part of these messages, or separate messages for Ports?
-
-public class DeviceAntiEntropyAdvertisement
-    extends AntiEntropyAdvertisement<DeviceId> {
-
-
-    public DeviceAntiEntropyAdvertisement(NodeId sender,
-            Map<DeviceId, Timestamp> advertisement) {
-        super(sender, advertisement);
-    }
-
-    // May need to add ProviderID, etc.
-    public static DeviceAntiEntropyAdvertisement create(
-            NodeId self,
-            Collection<VersionedValue<Device>> localValues) {
-
-        Map<DeviceId, Timestamp> ads = new HashMap<>(localValues.size());
-        for (VersionedValue<Device> e : localValues) {
-            ads.put(e.entity().id(), e.timestamp());
-        }
-        return new DeviceAntiEntropyAdvertisement(self, ads);
-    }
-
-    // For serializer
-    protected DeviceAntiEntropyAdvertisement() {}
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
deleted file mode 100644
index 34dc873..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package org.onlab.onos.store.device.impl;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.VersionedValue;
-import org.onlab.onos.store.common.impl.AntiEntropyReply;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-public class DeviceAntiEntropyReply
-    extends AntiEntropyReply<DeviceId, VersionedValue<Device>> {
-
-
-    public DeviceAntiEntropyReply(NodeId sender,
-            Map<DeviceId, VersionedValue<Device>> suggestion,
-            Set<DeviceId> request) {
-        super(sender, suggestion, request);
-    }
-
-    /**
-     * Creates a reply to Anti-Entropy advertisement.
-     *
-     * @param advertisement to respond to
-     * @param self node identifier representing local node
-     * @param localValues local values held on this node
-     * @return reply message
-     */
-    public static DeviceAntiEntropyReply reply(
-            DeviceAntiEntropyAdvertisement advertisement,
-            NodeId self,
-            Collection<VersionedValue<Device>> localValues
-            ) {
-
-        ImmutableMap<DeviceId, Timestamp> ads = advertisement.advertisement();
-
-        ImmutableMap.Builder<DeviceId, VersionedValue<Device>>
-            sug = ImmutableMap.builder();
-
-        Set<DeviceId> req = new HashSet<>(ads.keySet());
-
-        for (VersionedValue<Device> e : localValues) {
-            final DeviceId id = e.entity().id();
-            final Timestamp local = e.timestamp();
-            final Timestamp theirs = ads.get(id);
-            if (theirs == null) {
-                // they don't have it, suggest
-                sug.put(id, e);
-                // don't need theirs
-                req.remove(id);
-            } else if (local.compareTo(theirs) < 0) {
-                // they got older one, suggest
-                sug.put(id, e);
-                // don't need theirs
-                req.remove(id);
-            } else if (local.equals(theirs)) {
-                // same, don't need theirs
-                req.remove(id);
-            }
-        }
-
-        return new DeviceAntiEntropyReply(self, sug.build(), req);
-    }
-
-    /**
-     * Creates a reply to request for values held locally.
-     *
-     * @param requests message containing the request
-     * @param self node identifier representing local node
-     * @param localValues local valeds held on this node
-     * @return reply message
-     */
-    public static DeviceAntiEntropyReply reply(
-            DeviceAntiEntropyReply requests,
-            NodeId self,
-            Map<DeviceId, VersionedValue<Device>> localValues
-            ) {
-
-        Set<DeviceId> reqs = requests.request();
-
-        Map<DeviceId, VersionedValue<Device>> requested = new HashMap<>(reqs.size());
-        for (DeviceId id : reqs) {
-            final VersionedValue<Device> value = localValues.get(id);
-            if (value != null) {
-                requested.put(id, value);
-            }
-        }
-
-        Set<DeviceId> empty = ImmutableSet.of();
-        return new DeviceAntiEntropyReply(self, requested, empty);
-    }
-
-    // For serializer
-    protected DeviceAntiEntropyReply() {}
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
index 6f4fb96..e1e3692 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
@@ -10,12 +10,12 @@
 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.cluster.MastershipTerm;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.store.ClockProviderService;
 import org.onlab.onos.store.ClockService;
 import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.common.impl.DeviceMastershipBasedTimestamp;
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
 import org.slf4j.Logger;
 
 /**
@@ -29,7 +29,7 @@
 
     // TODO: Implement per device ticker that is reset to 0 at the beginning of a new term.
     private final AtomicInteger ticker = new AtomicInteger(0);
-    private ConcurrentMap<DeviceId, DeviceMastershipTerm> deviceMastershipTerms = new ConcurrentHashMap<>();
+    private ConcurrentMap<DeviceId, MastershipTerm> deviceMastershipTerms = new ConcurrentHashMap<>();
 
     @Activate
     public void activate() {
@@ -43,15 +43,15 @@
 
     @Override
     public Timestamp getTimestamp(DeviceId deviceId) {
-        DeviceMastershipTerm term = deviceMastershipTerms.get(deviceId);
+        MastershipTerm term = deviceMastershipTerms.get(deviceId);
         if (term == null) {
             throw new IllegalStateException("Requesting timestamp for a deviceId without mastership");
         }
-        return new DeviceMastershipBasedTimestamp(term.termNumber(), ticker.incrementAndGet());
+        return new MastershipBasedTimestamp(term.termNumber(), ticker.incrementAndGet());
     }
 
     @Override
-    public void setMastershipTerm(DeviceId deviceId, DeviceMastershipTerm term) {
+    public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
         deviceMastershipTerms.put(deviceId, term);
     }
 }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceDescriptions.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceDescriptions.java
new file mode 100644
index 0000000..f7fd7bc
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceDescriptions.java
@@ -0,0 +1,91 @@
+package org.onlab.onos.store.device.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.onos.net.DefaultAnnotations.union;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DefaultPortDescription;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+/*
+ * Collection of Description of a Device and Ports, given from a Provider.
+ */
+class DeviceDescriptions {
+
+    private volatile Timestamped<DeviceDescription> deviceDesc;
+
+    private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
+
+    public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
+        this.deviceDesc = checkNotNull(desc);
+        this.portDescs = new ConcurrentHashMap<>();
+    }
+
+    public Timestamp getLatestTimestamp() {
+        Timestamp latest = deviceDesc.timestamp();
+        for (Timestamped<PortDescription> desc : portDescs.values()) {
+            if (desc.timestamp().compareTo(latest) > 0) {
+                latest = desc.timestamp();
+            }
+        }
+        return latest;
+    }
+
+    public Timestamped<DeviceDescription> getDeviceDesc() {
+        return deviceDesc;
+    }
+
+    public Timestamped<PortDescription> getPortDesc(PortNumber number) {
+        return portDescs.get(number);
+    }
+
+    public Map<PortNumber, Timestamped<PortDescription>> getPortDescs() {
+        return Collections.unmodifiableMap(portDescs);
+    }
+
+    /**
+     * Puts DeviceDescription, merging annotations as necessary.
+     *
+     * @param newDesc new DeviceDescription
+     */
+    public synchronized void putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
+        Timestamped<DeviceDescription> oldOne = deviceDesc;
+        Timestamped<DeviceDescription> newOne = newDesc;
+        if (oldOne != null) {
+            SparseAnnotations merged = union(oldOne.value().annotations(),
+                                             newDesc.value().annotations());
+            newOne = new Timestamped<DeviceDescription>(
+                    new DefaultDeviceDescription(newDesc.value(), merged),
+                    newDesc.timestamp());
+        }
+        deviceDesc = newOne;
+    }
+
+    /**
+     * Puts PortDescription, merging annotations as necessary.
+     *
+     * @param newDesc new PortDescription
+     */
+    public synchronized void putPortDesc(Timestamped<PortDescription> newDesc) {
+        Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
+        Timestamped<PortDescription> newOne = newDesc;
+        if (oldOne != null) {
+            SparseAnnotations merged = union(oldOne.value().annotations(),
+                                             newDesc.value().annotations());
+            newOne = new Timestamped<PortDescription>(
+                    new DefaultPortDescription(newDesc.value(), merged),
+                    newDesc.timestamp());
+        }
+        portDescs.put(newOne.value().portNumber(), newOne);
+    }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
index 06c70a0..782bc6c 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
@@ -5,8 +5,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-import org.apache.commons.lang3.concurrent.ConcurrentException;
-import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+import org.apache.commons.lang3.RandomUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -14,6 +13,8 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.AnnotationsUtil;
 import org.onlab.onos.net.DefaultAnnotations;
 import org.onlab.onos.net.DefaultDevice;
@@ -23,9 +24,6 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Port;
 import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.SparseAnnotations;
-import org.onlab.onos.net.device.DefaultDeviceDescription;
-import org.onlab.onos.net.device.DefaultPortDescription;
 import org.onlab.onos.net.device.DeviceDescription;
 import org.onlab.onos.net.device.DeviceEvent;
 import org.onlab.onos.net.device.DeviceStore;
@@ -35,21 +33,25 @@
 import org.onlab.onos.store.AbstractStore;
 import org.onlab.onos.store.ClockService;
 import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.Timestamped;
 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
-import org.onlab.onos.store.common.impl.DeviceMastershipBasedTimestamp;
-import org.onlab.onos.store.common.impl.MastershipBasedTimestampSerializer;
-import org.onlab.onos.store.serializers.KryoPoolUtil;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.common.impl.Timestamped;
+import org.onlab.onos.store.device.impl.peermsg.DeviceAntiEntropyAdvertisement;
+import org.onlab.onos.store.device.impl.peermsg.DeviceFragmentId;
+import org.onlab.onos.store.device.impl.peermsg.PortFragmentId;
 import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.onos.store.serializers.DistributedStoreSerializers;
 import org.onlab.util.KryoPool;
 import org.onlab.util.NewConcurrentHashMap;
 import org.slf4j.Logger;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -57,19 +59,21 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Predicates.notNull;
 import static org.onlab.onos.net.device.DeviceEvent.Type.*;
 import static org.slf4j.LoggerFactory.getLogger;
 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
 import static org.onlab.onos.net.DefaultAnnotations.merge;
-import static org.onlab.onos.net.DefaultAnnotations.union;
 import static com.google.common.base.Verify.verify;
+import static org.onlab.util.Tools.namedThreads;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.onos.store.device.impl.GossipDeviceStoreMessageSubjects.DEVICE_ADVERTISE;
+import static org.onlab.onos.store.common.impl.ControllerNodeToNodeId.toNodeId;
 
 // TODO: give me a better name
 /**
@@ -86,8 +90,9 @@
 
     public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
 
-    // TODO: Check if inner Map can be replaced with plain Map
+    // TODO: Check if inner Map can be replaced with plain Map.
     // innerMap is used to lock a Device, thus instance should never be replaced.
+
     // collection of Description given from various providers
     private final ConcurrentMap<DeviceId,
                             ConcurrentMap<ProviderId, DeviceDescriptions>>
@@ -117,21 +122,23 @@
         @Override
         protected void setupKryoPool() {
             serializerPool = KryoPool.newBuilder()
-                    .register(KryoPoolUtil.API)
+                    .register(DistributedStoreSerializers.COMMON)
+
                     .register(InternalDeviceEvent.class, new InternalDeviceEventSerializer())
                     .register(InternalDeviceOfflineEvent.class, new InternalDeviceOfflineEventSerializer())
                     .register(InternalDeviceRemovedEvent.class)
                     .register(InternalPortEvent.class, new InternalPortEventSerializer())
                     .register(InternalPortStatusEvent.class, new InternalPortStatusEventSerializer())
-                    .register(Timestamp.class)
-                    .register(Timestamped.class)
-                    .register(DeviceMastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
+                    .register(DeviceAntiEntropyAdvertisement.class)
+                    .register(DeviceFragmentId.class)
+                    .register(PortFragmentId.class)
                     .build()
                     .populate(1);
         }
-
     };
 
+    private ScheduledExecutorService executor;
+
     @Activate
     public void activate() {
         clusterCommunicator.addSubscriber(
@@ -144,11 +151,35 @@
                 GossipDeviceStoreMessageSubjects.PORT_UPDATE, new InternalPortEventListener());
         clusterCommunicator.addSubscriber(
                 GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener());
+        clusterCommunicator.addSubscriber(
+                GossipDeviceStoreMessageSubjects.DEVICE_ADVERTISE, new InternalDeviceAdvertisementListener());
+
+        executor =
+                newSingleThreadScheduledExecutor(namedThreads("anti-entropy-%d"));
+
+        // TODO: Make these configurable
+        long initialDelaySec = 5;
+        long periodSec = 5;
+        // start anti-entropy thread
+        executor.scheduleAtFixedRate(new SendAdvertisementTask(),
+                    initialDelaySec, periodSec, TimeUnit.SECONDS);
+
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
+
+        executor.shutdownNow();
+        try {
+            boolean timedout = executor.awaitTermination(5, TimeUnit.SECONDS);
+            if (timedout) {
+                log.error("Timeout during executor shutdown");
+            }
+        } catch (InterruptedException e) {
+            log.error("Error during executor shutdown", e);
+        }
+
         deviceDescs.clear();
         devices.clear();
         devicePorts.clear();
@@ -543,14 +574,14 @@
 
             final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
             if (existingPortDesc == null ||
-                deltaDesc == existingPortDesc ||
                 deltaDesc.isNewer(existingPortDesc)) {
                 // on new port or valid update
                 // update description
                 descs.putPortDesc(deltaDesc);
                 newPort = composePort(device, number, descsMap);
             } else {
-                // outdated event, ignored.
+                // same or outdated event, ignored.
+                log.trace("ignore same or outdated {} >= {}", existingPortDesc, deltaDesc);
                 return null;
             }
 
@@ -627,6 +658,14 @@
         }
     }
 
+    /**
+     * Checks if given timestamp is superseded by removal request
+     * with more recent timestamp.
+     *
+     * @param deviceId identifier of a device
+     * @param timestampToCheck timestamp of an event to check
+     * @return true if device is already removed
+     */
     private boolean isDeviceRemoved(DeviceId deviceId, Timestamp timestampToCheck) {
         Timestamp removalTimestamp = removalRequest.get(deviceId);
         if (removalTimestamp != null &&
@@ -667,7 +706,7 @@
                 continue;
             }
             // TODO: should keep track of Description timestamp
-            // and only merge conflicting keys when timestamp is newer
+            // and only merge conflicting keys when timestamp is newer.
             // Currently assuming there will never be a key conflict between
             // providers
 
@@ -708,7 +747,7 @@
                 continue;
             }
             // TODO: should keep track of Description timestamp
-            // and only merge conflicting keys when timestamp is newer
+            // and only merge conflicting keys when timestamp is newer.
             // Currently assuming there will never be a key conflict between
             // providers
 
@@ -745,129 +784,258 @@
         return providerDescs.get(pid);
     }
 
-    public static final class InitDeviceDescs
-        implements ConcurrentInitializer<DeviceDescriptions> {
-
-        private final Timestamped<DeviceDescription> deviceDesc;
-
-        public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
-            this.deviceDesc = checkNotNull(deviceDesc);
-        }
-        @Override
-        public DeviceDescriptions get() throws ConcurrentException {
-            return new DeviceDescriptions(deviceDesc);
-        }
+    // TODO: should we be throwing exception?
+    private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) throws IOException {
+        ClusterMessage message = new ClusterMessage(
+                clusterService.getLocalNode().id(),
+                subject,
+                SERIALIZER.encode(event));
+        clusterCommunicator.unicast(message, recipient);
     }
 
-
-    /**
-     * Collection of Description of a Device and it's Ports given from a Provider.
-     */
-    public static class DeviceDescriptions {
-
-        private final AtomicReference<Timestamped<DeviceDescription>> deviceDesc;
-        private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
-
-        public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
-            this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
-            this.portDescs = new ConcurrentHashMap<>();
-        }
-
-        Timestamp getLatestTimestamp() {
-            Timestamp latest = deviceDesc.get().timestamp();
-            for (Timestamped<PortDescription> desc : portDescs.values()) {
-                if (desc.timestamp().compareTo(latest) > 0) {
-                    latest = desc.timestamp();
-                }
-            }
-            return latest;
-        }
-
-        public Timestamped<DeviceDescription> getDeviceDesc() {
-            return deviceDesc.get();
-        }
-
-        public Timestamped<PortDescription> getPortDesc(PortNumber number) {
-            return portDescs.get(number);
-        }
-
-        /**
-         * Puts DeviceDescription, merging annotations as necessary.
-         *
-         * @param newDesc new DeviceDescription
-         * @return previous DeviceDescription
-         */
-        public synchronized Timestamped<DeviceDescription> putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
-            Timestamped<DeviceDescription> oldOne = deviceDesc.get();
-            Timestamped<DeviceDescription> newOne = newDesc;
-            if (oldOne != null) {
-                SparseAnnotations merged = union(oldOne.value().annotations(),
-                                                 newDesc.value().annotations());
-                newOne = new Timestamped<DeviceDescription>(
-                        new DefaultDeviceDescription(newDesc.value(), merged),
-                        newDesc.timestamp());
-            }
-            return deviceDesc.getAndSet(newOne);
-        }
-
-        /**
-         * Puts PortDescription, merging annotations as necessary.
-         *
-         * @param newDesc new PortDescription
-         * @return previous PortDescription
-         */
-        public synchronized Timestamped<PortDescription> putPortDesc(Timestamped<PortDescription> newDesc) {
-            Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
-            Timestamped<PortDescription> newOne = newDesc;
-            if (oldOne != null) {
-                SparseAnnotations merged = union(oldOne.value().annotations(),
-                                                 newDesc.value().annotations());
-                newOne = new Timestamped<PortDescription>(
-                        new DefaultPortDescription(newDesc.value(), merged),
-                        newDesc.timestamp());
-            }
-            return portDescs.put(newOne.value().portNumber(), newOne);
-        }
+    // TODO: should we be throwing exception?
+    private void broadcastMessage(MessageSubject subject, Object event) throws IOException {
+        ClusterMessage message = new ClusterMessage(
+                clusterService.getLocalNode().id(),
+                subject,
+                SERIALIZER.encode(event));
+        clusterCommunicator.broadcast(message);
     }
 
     private void notifyPeers(InternalDeviceEvent event) throws IOException {
-        ClusterMessage message = new ClusterMessage(
-                clusterService.getLocalNode().id(),
-                GossipDeviceStoreMessageSubjects.DEVICE_UPDATE,
-                SERIALIZER.encode(event));
-        clusterCommunicator.broadcast(message);
+        broadcastMessage(GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, event);
     }
 
     private void notifyPeers(InternalDeviceOfflineEvent event) throws IOException {
-        ClusterMessage message = new ClusterMessage(
-                clusterService.getLocalNode().id(),
-                GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE,
-                SERIALIZER.encode(event));
-        clusterCommunicator.broadcast(message);
+        broadcastMessage(GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE, event);
     }
 
     private void notifyPeers(InternalDeviceRemovedEvent event) throws IOException {
-        ClusterMessage message = new ClusterMessage(
-                clusterService.getLocalNode().id(),
-                GossipDeviceStoreMessageSubjects.DEVICE_REMOVED,
-                SERIALIZER.encode(event));
-        clusterCommunicator.broadcast(message);
+        broadcastMessage(GossipDeviceStoreMessageSubjects.DEVICE_REMOVED, event);
     }
 
     private void notifyPeers(InternalPortEvent event) throws IOException {
-        ClusterMessage message = new ClusterMessage(
-                clusterService.getLocalNode().id(),
-                GossipDeviceStoreMessageSubjects.PORT_UPDATE,
-                SERIALIZER.encode(event));
-        clusterCommunicator.broadcast(message);
+        broadcastMessage(GossipDeviceStoreMessageSubjects.PORT_UPDATE, event);
     }
 
     private void notifyPeers(InternalPortStatusEvent event) throws IOException {
-        ClusterMessage message = new ClusterMessage(
-                clusterService.getLocalNode().id(),
-                GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE,
-                SERIALIZER.encode(event));
-        clusterCommunicator.broadcast(message);
+        broadcastMessage(GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, event);
+    }
+
+    private void notifyPeer(NodeId recipient, InternalDeviceEvent event) {
+        try {
+            unicastMessage(recipient, GossipDeviceStoreMessageSubjects.DEVICE_UPDATE, event);
+        } catch (IOException e) {
+            log.error("Failed to send" + event + " to " + recipient, e);
+        }
+    }
+
+    private void notifyPeer(NodeId recipient, InternalDeviceOfflineEvent event) {
+        try {
+            unicastMessage(recipient, GossipDeviceStoreMessageSubjects.DEVICE_OFFLINE, event);
+        } catch (IOException e) {
+            log.error("Failed to send" + event + " to " + recipient, e);
+        }
+    }
+
+    private void notifyPeer(NodeId recipient, InternalDeviceRemovedEvent event) {
+        try {
+            unicastMessage(recipient, GossipDeviceStoreMessageSubjects.DEVICE_REMOVED, event);
+        } catch (IOException e) {
+            log.error("Failed to send" + event + " to " + recipient, e);
+        }
+    }
+
+    private void notifyPeer(NodeId recipient, InternalPortEvent event) {
+        try {
+            unicastMessage(recipient, GossipDeviceStoreMessageSubjects.PORT_UPDATE, event);
+        } catch (IOException e) {
+            log.error("Failed to send" + event + " to " + recipient, e);
+        }
+    }
+
+    private void notifyPeer(NodeId recipient, InternalPortStatusEvent event) {
+        try {
+            unicastMessage(recipient, GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, event);
+        } catch (IOException e) {
+            log.error("Failed to send" + event + " to " + recipient, e);
+        }
+    }
+
+    private DeviceAntiEntropyAdvertisement createAdvertisement() {
+        final NodeId self = clusterService.getLocalNode().id();
+
+        Map<DeviceFragmentId, Timestamp> devices = new HashMap<>(deviceDescs.size());
+        final int portsPerDevice = 8; // random guess to minimize reallocation
+        Map<PortFragmentId, Timestamp> ports = new HashMap<>(devices.size() * portsPerDevice);
+        Map<DeviceId, Timestamp> offline = new HashMap<>(devices.size());
+
+        for (Entry<DeviceId, ConcurrentMap<ProviderId, DeviceDescriptions>>
+            provs : deviceDescs.entrySet()) {
+
+            final DeviceId deviceId = provs.getKey();
+            final ConcurrentMap<ProviderId, DeviceDescriptions> devDescs = provs.getValue();
+            synchronized (devDescs) {
+
+                offline.put(deviceId, this.offline.get(deviceId));
+
+                for (Entry<ProviderId, DeviceDescriptions>
+                        prov : devDescs.entrySet()) {
+
+                    final ProviderId provId = prov.getKey();
+                    final DeviceDescriptions descs = prov.getValue();
+
+                    devices.put(new DeviceFragmentId(deviceId, provId),
+                            descs.getDeviceDesc().timestamp());
+
+                    for (Entry<PortNumber, Timestamped<PortDescription>>
+                            portDesc : descs.getPortDescs().entrySet()) {
+
+                        final PortNumber number = portDesc.getKey();
+                        ports.put(new PortFragmentId(deviceId, provId, number),
+                                portDesc.getValue().timestamp());
+                    }
+                }
+            }
+        }
+
+        return new DeviceAntiEntropyAdvertisement(self, devices, ports, offline);
+    }
+
+    /**
+     * Responds to anti-entropy advertisement message.
+     * <P>
+     * Notify sender about out-dated information using regular replication message.
+     * Send back advertisement to sender if not in sync.
+     *
+     * @param advertisement to respond to
+     */
+    private void handleAdvertisement(DeviceAntiEntropyAdvertisement advertisement) {
+
+        final NodeId sender = advertisement.sender();
+
+        Map<DeviceFragmentId, Timestamp> devAds = new HashMap<>(advertisement.deviceFingerPrints());
+        Map<PortFragmentId, Timestamp> portAds = new HashMap<>(advertisement.ports());
+        Map<DeviceId, Timestamp> offlineAds = new HashMap<>(advertisement.offline());
+
+        // Fragments to request
+        Collection<DeviceFragmentId> reqDevices = new ArrayList<>();
+        Collection<PortFragmentId> reqPorts = new ArrayList<>();
+
+        for (Entry<DeviceId, ConcurrentMap<ProviderId, DeviceDescriptions>> de : deviceDescs.entrySet()) {
+            final DeviceId deviceId = de.getKey();
+            final Map<ProviderId, DeviceDescriptions> lDevice = de.getValue();
+
+            synchronized (lDevice) {
+                // latestTimestamp across provider
+                // Note: can be null initially
+                Timestamp localLatest = offline.get(deviceId);
+
+                // handle device Ads
+                for (Entry<ProviderId, DeviceDescriptions> prov : lDevice.entrySet()) {
+                    final ProviderId provId = prov.getKey();
+                    final DeviceDescriptions lDeviceDescs = prov.getValue();
+
+                    final DeviceFragmentId devFragId = new DeviceFragmentId(deviceId, provId);
+
+
+                    Timestamped<DeviceDescription> lProvDevice = lDeviceDescs.getDeviceDesc();
+                    Timestamp advDevTimestamp = devAds.get(devFragId);
+
+                    if (advDevTimestamp == null || lProvDevice.isNewer(advDevTimestamp)) {
+                        // remote does not have it or outdated, suggest
+                        notifyPeer(sender, new InternalDeviceEvent(provId, deviceId, lProvDevice));
+                    } else if (!lProvDevice.timestamp().equals(advDevTimestamp)) {
+                        // local is outdated, request
+                        reqDevices.add(devFragId);
+                    }
+
+                    // handle port Ads
+                    for (Entry<PortNumber, Timestamped<PortDescription>>
+                            pe : lDeviceDescs.getPortDescs().entrySet()) {
+
+                        final PortNumber num = pe.getKey();
+                        final Timestamped<PortDescription> lPort = pe.getValue();
+
+                        final PortFragmentId portFragId = new PortFragmentId(deviceId, provId, num);
+
+                        Timestamp advPortTimestamp = portAds.get(portFragId);
+                        if (advPortTimestamp == null || lPort.isNewer(advPortTimestamp)) {
+                            // remote does not have it or outdated, suggest
+                            notifyPeer(sender, new InternalPortStatusEvent(provId, deviceId, lPort));
+                        } else if (!lPort.timestamp().equals(advPortTimestamp)) {
+                            // local is outdated, request
+                            log.trace("need update {} < {}", lPort.timestamp(), advPortTimestamp);
+                            reqPorts.add(portFragId);
+                        }
+
+                        // remove port Ad already processed
+                        portAds.remove(portFragId);
+                    } // end local port loop
+
+                    // remove device Ad already processed
+                    devAds.remove(devFragId);
+
+                    // find latest and update
+                    final Timestamp providerLatest = lDeviceDescs.getLatestTimestamp();
+                    if (localLatest == null ||
+                        providerLatest.compareTo(localLatest) > 0) {
+                        localLatest = providerLatest;
+                    }
+                } // end local provider loop
+
+                // checking if remote timestamp is more recent.
+                Timestamp rOffline = offlineAds.get(deviceId);
+                if (rOffline != null &&
+                    rOffline.compareTo(localLatest) > 0) {
+                    // remote offline timestamp suggests that the
+                    // device is off-line
+                    markOfflineInternal(deviceId, rOffline);
+                }
+
+                Timestamp lOffline = offline.get(deviceId);
+                if (lOffline != null && rOffline == null) {
+                    // locally offline, but remote is online, suggest offline
+                    notifyPeer(sender, new InternalDeviceOfflineEvent(deviceId, lOffline));
+                }
+
+                // remove device offline Ad already processed
+                offlineAds.remove(deviceId);
+            } // end local device loop
+        } // device lock
+
+        // If there is any Ads left, request them
+        log.trace("Ads left {}, {}", devAds, portAds);
+        reqDevices.addAll(devAds.keySet());
+        reqPorts.addAll(portAds.keySet());
+
+        if (reqDevices.isEmpty() && reqPorts.isEmpty()) {
+            log.trace("Nothing to request to remote peer {}", sender);
+            return;
+        }
+
+        log.info("Need to sync {} {}", reqDevices, reqPorts);
+
+        // 2-way Anti-Entropy for now
+        try {
+            unicastMessage(sender, DEVICE_ADVERTISE, createAdvertisement());
+        } catch (IOException e) {
+            log.error("Failed to send response advertisement to " + sender, e);
+        }
+
+// Sketch of 3-way Anti-Entropy
+//        DeviceAntiEntropyRequest request = new DeviceAntiEntropyRequest(self, reqDevices, reqPorts);
+//        ClusterMessage message = new ClusterMessage(
+//                clusterService.getLocalNode().id(),
+//                GossipDeviceStoreMessageSubjects.DEVICE_REQUEST,
+//                SERIALIZER.encode(request));
+//
+//        try {
+//            clusterCommunicator.unicast(message, advertisement.sender());
+//        } catch (IOException e) {
+//            log.error("Failed to send advertisement reply to "
+//                      + advertisement.sender(), e);
+//        }
     }
 
     private void notifyDelegateIfNotNull(DeviceEvent event) {
@@ -876,6 +1044,54 @@
         }
     }
 
+    private final class SendAdvertisementTask implements Runnable {
+
+        @Override
+        public void run() {
+            if (Thread.currentThread().isInterrupted()) {
+                log.info("Interrupted, quitting");
+                return;
+            }
+
+            try {
+                final NodeId self = clusterService.getLocalNode().id();
+                Set<ControllerNode> nodes = clusterService.getNodes();
+
+                ImmutableList<NodeId> nodeIds = FluentIterable.from(nodes)
+                        .transform(toNodeId())
+                        .toList();
+
+                if (nodeIds.size() == 1 && nodeIds.get(0).equals(self)) {
+                    log.info("No other peers in the cluster.");
+                    return;
+                }
+
+                NodeId peer;
+                do {
+                    int idx = RandomUtils.nextInt(0, nodeIds.size());
+                    peer = nodeIds.get(idx);
+                } while (peer.equals(self));
+
+                DeviceAntiEntropyAdvertisement ad = createAdvertisement();
+
+                if (Thread.currentThread().isInterrupted()) {
+                    log.info("Interrupted, quitting");
+                    return;
+                }
+
+                try {
+                    unicastMessage(peer, DEVICE_ADVERTISE, ad);
+                } catch (IOException e) {
+                    log.error("Failed to send anti-entropy advertisement", e);
+                    return;
+                }
+            } catch (Exception e) {
+                // catch all Exception to avoid Scheduled task being suppressed.
+                log.error("Exception thrown while sending advertisement", e);
+            }
+        }
+    }
+
     private class InternalDeviceEventListener implements ClusterMessageHandler {
         @Override
         public void handle(ClusterMessage message) {
@@ -940,6 +1156,7 @@
 
             log.info("Received port status update event from peer: {}", message.sender());
             InternalPortStatusEvent event = (InternalPortStatusEvent) SERIALIZER.decode(message.payload());
+            log.info("{}", event);
 
             ProviderId providerId = event.providerId();
             DeviceId deviceId = event.deviceId();
@@ -948,4 +1165,15 @@
             notifyDelegateIfNotNull(updatePortStatusInternal(providerId, deviceId, portDescription));
         }
     }
+
+    private final class InternalDeviceAdvertisementListener
+        implements ClusterMessageHandler {
+
+        @Override
+        public void handle(ClusterMessage message) {
+            log.info("Received Device advertisement from peer: {}", message.sender());
+            DeviceAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload());
+            handleAdvertisement(advertisement);
+        }
+    }
 }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
index 5272182..3168368 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStoreMessageSubjects.java
@@ -2,6 +2,7 @@
 
 import org.onlab.onos.store.cluster.messaging.MessageSubject;
 
+// TODO: add prefix to assure uniqueness.
 /**
  * MessageSubjects used by GossipDeviceStore peer-peer communication.
  */
@@ -14,4 +15,8 @@
     public static final MessageSubject DEVICE_REMOVED = new MessageSubject("peer-device-removed");
     public static final MessageSubject PORT_UPDATE = new MessageSubject("peer-port-update");
     public static final MessageSubject PORT_STATUS_UPDATE = new MessageSubject("peer-port-status-update");
+
+    public static final MessageSubject DEVICE_ADVERTISE = new MessageSubject("peer-device-advertisements");
+    // to be used with 3-way anti-entropy process
+    public static final MessageSubject DEVICE_REQUEST = new MessageSubject("peer-device-request");
 }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InitDeviceDescs.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InitDeviceDescs.java
new file mode 100644
index 0000000..2de2364
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InitDeviceDescs.java
@@ -0,0 +1,23 @@
+package org.onlab.onos.store.device.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.commons.lang3.concurrent.ConcurrentException;
+import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+// FIXME: consider removing this class
+public final class InitDeviceDescs
+    implements ConcurrentInitializer<DeviceDescriptions> {
+
+    private final Timestamped<DeviceDescription> deviceDesc;
+
+    public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
+        this.deviceDesc = checkNotNull(deviceDesc);
+    }
+    @Override
+    public DeviceDescriptions get() throws ConcurrentException {
+        return new DeviceDescriptions(deviceDesc);
+    }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java
index 1deb5d5..344fe73 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEvent.java
@@ -3,7 +3,9 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.DeviceDescription;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.Timestamped;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+import com.google.common.base.MoreObjects;
 
 /**
  * Information published by GossipDeviceStore to notify peers of a device
@@ -36,6 +38,15 @@
         return deviceDescription;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("providerId", providerId)
+                .add("deviceId", deviceId)
+                .add("deviceDescription", deviceDescription)
+                .toString();
+    }
+
     // for serializer
     protected InternalDeviceEvent() {
         this.providerId = null;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java
index 63ddda3..0d3d013 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceEventSerializer.java
@@ -3,7 +3,7 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.DeviceDescription;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.Timestamped;
+import org.onlab.onos.store.common.impl.Timestamped;
 
 import com.esotericsoftware.kryo.Kryo;
 import com.esotericsoftware.kryo.Serializer;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java
index d8942d6..4540efb 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceOfflineEvent.java
@@ -3,6 +3,8 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.store.Timestamp;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * Information published by GossipDeviceStore to notify peers of a device
  * going offline.
@@ -30,6 +32,14 @@
         return timestamp;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("deviceId", deviceId)
+                .add("timestamp", timestamp)
+                .toString();
+    }
+
     // for serializer
     @SuppressWarnings("unused")
     private InternalDeviceOfflineEvent() {
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java
index 6c8b905..42cb177 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalDeviceRemovedEvent.java
@@ -3,6 +3,8 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.store.Timestamp;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * Information published by GossipDeviceStore to notify peers of a device
  * being administratively removed.
@@ -30,6 +32,14 @@
         return timestamp;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("deviceId", deviceId)
+                .add("timestamp", timestamp)
+                .toString();
+    }
+
     // for serializer
     @SuppressWarnings("unused")
     private InternalDeviceRemovedEvent() {
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java
index ad4e380..d1fc73a 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEvent.java
@@ -5,7 +5,9 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.Timestamped;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+import com.google.common.base.MoreObjects;
 
 /**
  * Information published by GossipDeviceStore to notify peers of a port
@@ -38,6 +40,15 @@
         return portDescriptions;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("providerId", providerId)
+                .add("deviceId", deviceId)
+                .add("portDescriptions", portDescriptions)
+                .toString();
+    }
+
     // for serializer
     protected InternalPortEvent() {
         this.providerId = null;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java
index ede88f1..6fff395 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortEventSerializer.java
@@ -5,7 +5,7 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.Timestamped;
+import org.onlab.onos.store.common.impl.Timestamped;
 
 import com.esotericsoftware.kryo.Kryo;
 import com.esotericsoftware.kryo.Serializer;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java
index 00f9a9f..fd154da 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEvent.java
@@ -3,7 +3,9 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.Timestamped;
+import org.onlab.onos.store.common.impl.Timestamped;
+
+import com.google.common.base.MoreObjects;
 
 /**
  * Information published by GossipDeviceStore to notify peers of a port
@@ -36,6 +38,15 @@
         return portDescription;
     }
 
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("providerId", providerId)
+                .add("deviceId", deviceId)
+                .add("portDescription", portDescription)
+                .toString();
+    }
+
     // for serializer
     protected InternalPortStatusEvent() {
         this.providerId = null;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java
index 82fbb89..8f0c2b0 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/InternalPortStatusEventSerializer.java
@@ -3,7 +3,7 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.Timestamped;
+import org.onlab.onos.store.common.impl.Timestamped;
 
 import com.esotericsoftware.kryo.Kryo;
 import com.esotericsoftware.kryo.Serializer;
@@ -35,6 +35,7 @@
                                Class<InternalPortStatusEvent> type) {
         ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
         DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
+        @SuppressWarnings("unchecked")
         Timestamped<PortDescription> portDescription = (Timestamped<PortDescription>) kryo.readClassAndObject(input);
 
         return new InternalPortStatusEvent(providerId, deviceId, portDescription);
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceAntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceAntiEntropyAdvertisement.java
new file mode 100644
index 0000000..00873ad
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceAntiEntropyAdvertisement.java
@@ -0,0 +1,57 @@
+package org.onlab.onos.store.device.impl.peermsg;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.Timestamp;
+
+
+/**
+ * Device Advertisement message.
+ */
+public class DeviceAntiEntropyAdvertisement {
+
+    private final NodeId sender;
+    private final Map<DeviceFragmentId, Timestamp> deviceFingerPrints;
+    private final Map<PortFragmentId, Timestamp> portFingerPrints;
+    private final Map<DeviceId, Timestamp> offline;
+
+
+    public DeviceAntiEntropyAdvertisement(NodeId sender,
+                Map<DeviceFragmentId, Timestamp> devices,
+                Map<PortFragmentId, Timestamp> ports,
+                Map<DeviceId, Timestamp> offline) {
+        this.sender = checkNotNull(sender);
+        this.deviceFingerPrints = checkNotNull(devices);
+        this.portFingerPrints = checkNotNull(ports);
+        this.offline = checkNotNull(offline);
+    }
+
+    public NodeId sender() {
+        return sender;
+    }
+
+    public Map<DeviceFragmentId, Timestamp> deviceFingerPrints() {
+        return deviceFingerPrints;
+    }
+
+    public Map<PortFragmentId, Timestamp> ports() {
+        return portFingerPrints;
+    }
+
+    public Map<DeviceId, Timestamp> offline() {
+        return offline;
+    }
+
+    // For serializer
+    @SuppressWarnings("unused")
+    private DeviceAntiEntropyAdvertisement() {
+        this.sender = null;
+        this.deviceFingerPrints = null;
+        this.portFingerPrints = null;
+        this.offline = null;
+    }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceAntiEntropyRequest.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceAntiEntropyRequest.java
new file mode 100644
index 0000000..6f3096b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceAntiEntropyRequest.java
@@ -0,0 +1,46 @@
+package org.onlab.onos.store.device.impl.peermsg;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collection;
+
+import org.onlab.onos.cluster.NodeId;
+
+/**
+ * Message to request for other peers information.
+ */
+public class DeviceAntiEntropyRequest {
+
+    private final NodeId sender;
+    private final Collection<DeviceFragmentId> devices;
+    private final Collection<PortFragmentId> ports;
+
+    public DeviceAntiEntropyRequest(NodeId sender,
+                                   Collection<DeviceFragmentId> devices,
+                                   Collection<PortFragmentId> ports) {
+
+        this.sender = checkNotNull(sender);
+        this.devices = checkNotNull(devices);
+        this.ports = checkNotNull(ports);
+    }
+
+    public NodeId sender() {
+        return sender;
+    }
+
+    public Collection<DeviceFragmentId> devices() {
+        return devices;
+    }
+
+    public Collection<PortFragmentId> ports() {
+        return ports;
+    }
+
+    // For serializer
+    @SuppressWarnings("unused")
+    private DeviceAntiEntropyRequest() {
+        this.sender = null;
+        this.devices = null;
+        this.ports = null;
+    }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceFragmentId.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceFragmentId.java
new file mode 100644
index 0000000..d4fcda9
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/DeviceFragmentId.java
@@ -0,0 +1,54 @@
+package org.onlab.onos.store.device.impl.peermsg;
+
+import java.util.Objects;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.provider.ProviderId;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Identifier for DeviceDesctiption from a Provider.
+ */
+public final class DeviceFragmentId {
+    public final ProviderId providerId;
+    public final DeviceId deviceId;
+
+    public DeviceFragmentId(DeviceId deviceId, ProviderId providerId) {
+        this.providerId = providerId;
+        this.deviceId = deviceId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(providerId, deviceId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DeviceFragmentId)) {
+            return false;
+        }
+        DeviceFragmentId that = (DeviceFragmentId) obj;
+        return Objects.equals(this.deviceId, that.deviceId) &&
+               Objects.equals(this.providerId, that.providerId);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("providerId", providerId)
+                .add("deviceId", deviceId)
+                .toString();
+    }
+
+    // for serializer
+    @SuppressWarnings("unused")
+    private DeviceFragmentId() {
+        this.providerId = null;
+        this.deviceId = null;
+    }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/PortFragmentId.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/PortFragmentId.java
new file mode 100644
index 0000000..8e7bac3
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/PortFragmentId.java
@@ -0,0 +1,61 @@
+package org.onlab.onos.store.device.impl.peermsg;
+
+import java.util.Objects;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.provider.ProviderId;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Identifier for PortDescription from a Provider.
+ */
+public final class PortFragmentId {
+    public final ProviderId providerId;
+    public final DeviceId deviceId;
+    public final PortNumber portNumber;
+
+    public PortFragmentId(DeviceId deviceId, ProviderId providerId,
+                          PortNumber portNumber) {
+        this.providerId = providerId;
+        this.deviceId = deviceId;
+        this.portNumber = portNumber;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(providerId, deviceId, portNumber);
+    };
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PortFragmentId)) {
+            return false;
+        }
+        PortFragmentId that = (PortFragmentId) obj;
+        return Objects.equals(this.deviceId, that.deviceId) &&
+               Objects.equals(this.portNumber, that.portNumber) &&
+               Objects.equals(this.providerId, that.providerId);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("providerId", providerId)
+                .add("deviceId", deviceId)
+                .add("portNumber", portNumber)
+                .toString();
+    }
+
+    // for serializer
+    @SuppressWarnings("unused")
+    private PortFragmentId() {
+        this.providerId = null;
+        this.deviceId = null;
+        this.portNumber = null;
+    }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/package-info.java
new file mode 100644
index 0000000..5d9dc4b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/peermsg/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Structure and utilities used for inter-Node messaging.
+ */
+package org.onlab.onos.store.device.impl.peermsg;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
index 09820f4..9362156 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
@@ -1,26 +1,20 @@
 package org.onlab.onos.store.host.impl;
 
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
 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.Annotations;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultHost;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.HostLocation;
 import org.onlab.onos.net.host.HostDescription;
 import org.onlab.onos.net.host.HostEvent;
 import org.onlab.onos.net.host.HostStore;
@@ -33,10 +27,13 @@
 import org.onlab.packet.VlanId;
 import org.slf4j.Logger;
 
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.onlab.onos.net.host.HostEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Manages inventory of end-station hosts using trivial in-memory
@@ -46,13 +43,13 @@
 @Component(immediate = true)
 @Service
 public class DistributedHostStore
-extends AbstractStore<HostEvent, HostStoreDelegate>
-implements HostStore {
+        extends AbstractStore<HostEvent, HostStoreDelegate>
+        implements HostStore {
 
     private final Logger log = getLogger(getClass());
 
     // Host inventory
-    private final Map<HostId, Host> hosts = new ConcurrentHashMap<>();
+    private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
 
     // Hosts tracked by their location
     private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
@@ -72,8 +69,8 @@
 
     @Override
     public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
-            HostDescription hostDescription) {
-        Host host = hosts.get(hostId);
+                                        HostDescription hostDescription) {
+        StoredHost host = hosts.get(hostId);
         if (host == null) {
             return createHost(providerId, hostId, hostDescription);
         }
@@ -82,12 +79,12 @@
 
     // creates a new host and sends HOST_ADDED
     private HostEvent createHost(ProviderId providerId, HostId hostId,
-            HostDescription descr) {
-        DefaultHost newhost = new DefaultHost(providerId, hostId,
-                descr.hwAddress(),
-                descr.vlan(),
-                descr.location(),
-                descr.ipAddresses());
+                                 HostDescription descr) {
+        StoredHost newhost = new StoredHost(providerId, hostId,
+                                            descr.hwAddress(),
+                                            descr.vlan(),
+                                            descr.location(),
+                                            ImmutableSet.of(descr.ipAddress()));
         synchronized (this) {
             hosts.put(hostId, newhost);
             locations.put(descr.location(), newhost);
@@ -96,28 +93,24 @@
     }
 
     // checks for type of update to host, sends appropriate event
-    private HostEvent updateHost(ProviderId providerId, Host host,
-            HostDescription descr) {
-        DefaultHost updated;
+    private HostEvent updateHost(ProviderId providerId, StoredHost host,
+                                 HostDescription descr) {
         HostEvent event;
         if (!host.location().equals(descr.location())) {
-            updated = new DefaultHost(providerId, host.id(),
-                    host.mac(),
-                    host.vlan(),
-                    descr.location(),
-                    host.ipAddresses());
-            event = new HostEvent(HOST_MOVED, updated);
+            host.setLocation(descr.location());
+            return new HostEvent(HOST_MOVED, host);
+        }
 
-        } else if (!(host.ipAddresses().equals(descr.ipAddresses()))) {
-            updated = new DefaultHost(providerId, host.id(),
-                    host.mac(),
-                    host.vlan(),
-                    descr.location(),
-                    descr.ipAddresses());
-            event = new HostEvent(HOST_UPDATED, updated);
-        } else {
+        if (host.ipAddresses().contains(descr.ipAddress())) {
             return null;
         }
+
+        Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
+        addresses.add(descr.ipAddress());
+        StoredHost updated = new StoredHost(providerId, host.id(),
+                                            host.mac(), host.vlan(),
+                                            descr.location(), addresses);
+        event = new HostEvent(HOST_UPDATED, updated);
         synchronized (this) {
             hosts.put(host.id(), updated);
             locations.remove(host.location(), host);
@@ -145,7 +138,7 @@
 
     @Override
     public Iterable<Host> getHosts() {
-        return Collections.unmodifiableSet(new HashSet<>(hosts.values()));
+        return ImmutableSet.<Host>copyOf(hosts.values());
     }
 
     @Override
@@ -275,4 +268,35 @@
         return addresses;
     }
 
+    // Auxiliary extension to allow location to mutate.
+    private class StoredHost extends DefaultHost {
+        private HostLocation location;
+
+        /**
+         * Creates an end-station host using the supplied information.
+         *
+         * @param providerId  provider identity
+         * @param id          host identifier
+         * @param mac         host MAC address
+         * @param vlan        host VLAN identifier
+         * @param location    host location
+         * @param ips         host IP addresses
+         * @param annotations optional key/value annotations
+         */
+        public StoredHost(ProviderId providerId, HostId id,
+                          MacAddress mac, VlanId vlan, HostLocation location,
+                          Set<IpPrefix> ips, Annotations... annotations) {
+            super(providerId, id, mac, vlan, location, ips, annotations);
+            this.location = location;
+        }
+
+        void setLocation(HostLocation location) {
+            this.location = location;
+        }
+
+        @Override
+        public HostLocation location() {
+            return location;
+        }
+    }
 }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
index d00d76b..63245a3 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
@@ -31,7 +31,6 @@
 import org.onlab.onos.store.AbstractStore;
 import org.onlab.onos.store.ClockService;
 import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.VersionedValue;
 import org.slf4j.Logger;
 
 import com.google.common.collect.HashMultimap;
diff --git a/core/api/src/main/java/org/onlab/onos/store/VersionedValue.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/VersionedValue.java
similarity index 93%
rename from core/api/src/main/java/org/onlab/onos/store/VersionedValue.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/link/impl/VersionedValue.java
index 70602f6..391cd1d 100644
--- a/core/api/src/main/java/org/onlab/onos/store/VersionedValue.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/VersionedValue.java
@@ -1,7 +1,10 @@
-package org.onlab.onos.store;
+package org.onlab.onos.store.link.impl;
 
 import java.util.Objects;
 
+import org.onlab.onos.store.Timestamp;
+
+// TODO: remove once we stop using this
 /**
  * Wrapper class for a entity that is versioned
  * and can either be up or down.
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMessageSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/ClusterMessageSerializer.java
similarity index 95%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMessageSerializer.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/serializers/ClusterMessageSerializer.java
index d9a61b8..e20b4a6 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterMessageSerializer.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/ClusterMessageSerializer.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.cluster.messaging.impl;
+package org.onlab.onos.store.serializers;
 
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
@@ -35,4 +35,4 @@
         byte[] payload = input.readBytes(payloadSize);
         return new ClusterMessage(sender, subject, payload);
     }
-}
\ No newline at end of file
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/DistributedStoreSerializers.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/DistributedStoreSerializers.java
new file mode 100644
index 0000000..e0ba906
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/DistributedStoreSerializers.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
+import org.onlab.onos.store.common.impl.Timestamped;
+import org.onlab.util.KryoPool;
+
+public final class DistributedStoreSerializers {
+
+    /**
+     * KryoPool which can serialize ON.lab misc classes.
+     */
+    public static final KryoPool COMMON = KryoPool.newBuilder()
+            .register(KryoPoolUtil.API)
+            .register(Timestamped.class)
+            .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
+            .build();
+
+    // avoid instantiation
+    private DistributedStoreSerializers() {}
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java
new file mode 100644
index 0000000..516915e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java
@@ -0,0 +1,36 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+// To be used if Timestamp ever needs to cross bundle boundary.
+/**
+ * Kryo Serializer for {@link MastershipBasedTimestamp}.
+ */
+public class MastershipBasedTimestampSerializer extends Serializer<MastershipBasedTimestamp> {
+
+    /**
+     * Creates a serializer for {@link MastershipBasedTimestamp}.
+     */
+    public MastershipBasedTimestampSerializer() {
+        // non-null, immutable
+        super(false, true);
+    }
+
+    @Override
+    public void write(Kryo kryo, Output output, MastershipBasedTimestamp object) {
+        output.writeInt(object.termNumber());
+        output.writeInt(object.sequenceNumber());
+    }
+
+    @Override
+    public MastershipBasedTimestamp read(Kryo kryo, Input input, Class<MastershipBasedTimestamp> type) {
+        final int term = input.readInt();
+        final int sequence = input.readInt();
+        return new MastershipBasedTimestamp(term, sequence);
+    }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSubjectSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java
similarity index 93%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSubjectSerializer.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java
index d817ced..bb6b292 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSubjectSerializer.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MessageSubjectSerializer.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.cluster.messaging.impl;
+package org.onlab.onos.store.serializers;
 
 import org.onlab.onos.store.cluster.messaging.MessageSubject;
 
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
index 2c8ff35..ea63ef8 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
@@ -6,25 +6,26 @@
 
 import org.junit.Test;
 import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
 import org.onlab.util.KryoPool;
 
 import com.google.common.testing.EqualsTester;
 
 /**
- * Test of {@link DeviceMastershipBasedTimestamp}.
+ * Test of {@link MastershipBasedTimestamp}.
  */
 public class MastershipBasedTimestampTest {
 
-    private static final Timestamp TS_1_1 = new DeviceMastershipBasedTimestamp(1, 1);
-    private static final Timestamp TS_1_2 = new DeviceMastershipBasedTimestamp(1, 2);
-    private static final Timestamp TS_2_1 = new DeviceMastershipBasedTimestamp(2, 1);
-    private static final Timestamp TS_2_2 = new DeviceMastershipBasedTimestamp(2, 2);
+    private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
+    private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
+    private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
+    private static final Timestamp TS_2_2 = new MastershipBasedTimestamp(2, 2);
 
     @Test
     public final void testBasic() {
         final int termNumber = 5;
         final int sequenceNumber = 6;
-        DeviceMastershipBasedTimestamp ts = new DeviceMastershipBasedTimestamp(termNumber,
+        MastershipBasedTimestamp ts = new MastershipBasedTimestamp(termNumber,
                                                 sequenceNumber);
 
         assertEquals(termNumber, ts.termNumber());
@@ -34,7 +35,7 @@
     @Test
     public final void testCompareTo() {
         assertTrue(TS_1_1.compareTo(TS_1_1) == 0);
-        assertTrue(TS_1_1.compareTo(new DeviceMastershipBasedTimestamp(1, 1)) == 0);
+        assertTrue(TS_1_1.compareTo(new MastershipBasedTimestamp(1, 1)) == 0);
 
         assertTrue(TS_1_1.compareTo(TS_1_2) < 0);
         assertTrue(TS_1_2.compareTo(TS_1_1) > 0);
@@ -48,14 +49,14 @@
     @Test
     public final void testEqualsObject() {
         new EqualsTester()
-        .addEqualityGroup(new DeviceMastershipBasedTimestamp(1, 1),
-                          new DeviceMastershipBasedTimestamp(1, 1), TS_1_1)
-        .addEqualityGroup(new DeviceMastershipBasedTimestamp(1, 2),
-                          new DeviceMastershipBasedTimestamp(1, 2), TS_1_2)
-        .addEqualityGroup(new DeviceMastershipBasedTimestamp(2, 1),
-                          new DeviceMastershipBasedTimestamp(2, 1), TS_2_1)
-        .addEqualityGroup(new DeviceMastershipBasedTimestamp(2, 2),
-                          new DeviceMastershipBasedTimestamp(2, 2), TS_2_2)
+        .addEqualityGroup(new MastershipBasedTimestamp(1, 1),
+                          new MastershipBasedTimestamp(1, 1), TS_1_1)
+        .addEqualityGroup(new MastershipBasedTimestamp(1, 2),
+                          new MastershipBasedTimestamp(1, 2), TS_1_2)
+        .addEqualityGroup(new MastershipBasedTimestamp(2, 1),
+                          new MastershipBasedTimestamp(2, 1), TS_2_1)
+        .addEqualityGroup(new MastershipBasedTimestamp(2, 2),
+                          new MastershipBasedTimestamp(2, 2), TS_2_2)
         .testEquals();
     }
 
@@ -63,7 +64,7 @@
     public final void testKryoSerializable() {
         final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
         final KryoPool kryos = KryoPool.newBuilder()
-                .register(DeviceMastershipBasedTimestamp.class)
+                .register(MastershipBasedTimestamp.class)
                 .build();
 
         kryos.serialize(TS_2_1, buffer);
@@ -79,7 +80,7 @@
     public final void testKryoSerializableWithHandcraftedSerializer() {
         final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
         final KryoPool kryos = KryoPool.newBuilder()
-                .register(DeviceMastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
+                .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
                 .build();
 
         kryos.serialize(TS_1_2, buffer);
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
index 2a0faa8..4b44d40 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
@@ -6,7 +6,6 @@
 
 import org.junit.Test;
 import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.Timestamped;
 import org.onlab.util.KryoPool;
 
 import com.google.common.testing.EqualsTester;
@@ -16,9 +15,9 @@
  */
 public class TimestampedTest {
 
-    private static final Timestamp TS_1_1 = new DeviceMastershipBasedTimestamp(1, 1);
-    private static final Timestamp TS_1_2 = new DeviceMastershipBasedTimestamp(1, 2);
-    private static final Timestamp TS_2_1 = new DeviceMastershipBasedTimestamp(2, 1);
+    private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
+    private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
+    private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
 
     @Test
     public final void testHashCode() {
@@ -80,7 +79,7 @@
         final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
         final KryoPool kryos = KryoPool.newBuilder()
                 .register(Timestamped.class,
-                        DeviceMastershipBasedTimestamp.class)
+                        MastershipBasedTimestamp.class)
                 .build();
 
         Timestamped<String> original = new Timestamped<>("foobar", TS_1_1);
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
index d1060cc..fa42a6b 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
@@ -25,6 +25,7 @@
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.ControllerNode.State;
 import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.Annotations;
 import org.onlab.onos.net.DefaultAnnotations;
@@ -39,7 +40,6 @@
 import org.onlab.onos.net.device.DeviceEvent;
 import org.onlab.onos.net.device.DeviceStore;
 import org.onlab.onos.net.device.DeviceStoreDelegate;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.net.device.PortDescription;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.onos.store.ClockService;
@@ -113,8 +113,8 @@
         deviceClockManager.activate();
         clockService = deviceClockManager;
 
-        deviceClockManager.setMastershipTerm(DID1, DeviceMastershipTerm.of(MYSELF, 1));
-        deviceClockManager.setMastershipTerm(DID2, DeviceMastershipTerm.of(MYSELF, 2));
+        deviceClockManager.setMastershipTerm(DID1, MastershipTerm.of(MYSELF, 1));
+        deviceClockManager.setMastershipTerm(DID2, MastershipTerm.of(MYSELF, 2));
 
         ClusterCommunicationService clusterCommunicator = new TestClusterCommunicationService();
         ClusterService clusterService = new TestClusterService();
diff --git a/core/store/hz/cluster/pom.xml b/core/store/hz/cluster/pom.xml
index 95307f1..d183aed 100644
--- a/core/store/hz/cluster/pom.xml
+++ b/core/store/hz/cluster/pom.xml
@@ -46,10 +46,6 @@
           <groupId>com.hazelcast</groupId>
           <artifactId>hazelcast</artifactId>
         </dependency>
-        <dependency>
-          <groupId>de.javakaffee</groupId>
-          <artifactId>kryo-serializers</artifactId>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedDeviceMastershipStore.java b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
similarity index 88%
rename from core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedDeviceMastershipStore.java
rename to core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
index cdcf84b..71d42fa 100644
--- a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedDeviceMastershipStore.java
+++ b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
@@ -1,6 +1,6 @@
 package org.onlab.onos.store.cluster.impl;
 
-import static org.onlab.onos.net.device.DeviceMastershipEvent.Type.MASTER_CHANGED;
+import static org.onlab.onos.cluster.MastershipEvent.Type.MASTER_CHANGED;
 
 import java.util.Map;
 import java.util.Set;
@@ -12,13 +12,13 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.MastershipEvent;
+import org.onlab.onos.cluster.MastershipStore;
+import org.onlab.onos.cluster.MastershipStoreDelegate;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.device.DeviceMastershipEvent;
-import org.onlab.onos.net.device.DeviceMastershipStore;
-import org.onlab.onos.net.device.DeviceMastershipStoreDelegate;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.store.common.AbstractHazelcastStore;
 
 import com.google.common.collect.ImmutableSet;
@@ -32,9 +32,9 @@
  */
 @Component(immediate = true)
 @Service
-public class DistributedDeviceMastershipStore
-extends AbstractHazelcastStore<DeviceMastershipEvent, DeviceMastershipStoreDelegate>
-implements DeviceMastershipStore {
+public class DistributedMastershipStore
+extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
+implements MastershipStore {
 
     //arbitrary lock name
     private static final String LOCK = "lock";
@@ -100,7 +100,7 @@
     }
 
     @Override
-    public DeviceMastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
+    public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
         byte [] did = serialize(deviceId);
         byte [] nid = serialize(nodeId);
 
@@ -123,12 +123,12 @@
                     masters.put(did, nid);
                     evict(nid, did);
                     updateTerm(did);
-                    return new DeviceMastershipEvent(MASTER_CHANGED, deviceId, nodeId);
+                    return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
                 case NONE:
                     masters.put(did, nid);
                     evict(nid, did);
                     updateTerm(did);
-                    return new DeviceMastershipEvent(MASTER_CHANGED, deviceId, nodeId);
+                    return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
                 default:
                     log.warn("unknown Mastership Role {}", role);
                     return null;
@@ -191,21 +191,21 @@
     }
 
     @Override
-    public DeviceMastershipTerm getTermFor(DeviceId deviceId) {
+    public MastershipTerm getTermFor(DeviceId deviceId) {
         byte[] did = serialize(deviceId);
         if ((masters.get(did) == null) ||
                 (terms.get(did) == null)) {
             return null;
         }
-        return DeviceMastershipTerm.of(
+        return MastershipTerm.of(
                 (NodeId) deserialize(masters.get(did)), terms.get(did));
     }
 
     @Override
-    public DeviceMastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
+    public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
         byte [] did = serialize(deviceId);
         byte [] nid = serialize(nodeId);
-        DeviceMastershipEvent event = null;
+        MastershipEvent event = null;
 
         ILock lock = theInstance.getLock(LOCK);
         lock.lock();
@@ -231,10 +231,10 @@
     }
 
     @Override
-    public DeviceMastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
+    public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
         byte [] did = serialize(deviceId);
         byte [] nid = serialize(nodeId);
-        DeviceMastershipEvent event = null;
+        MastershipEvent event = null;
 
         ILock lock = theInstance.getLock(LOCK);
         lock.lock();
@@ -260,7 +260,7 @@
     }
 
     //helper to fetch a new master candidate for a given device.
-    private DeviceMastershipEvent reelect(NodeId current, DeviceId deviceId) {
+    private MastershipEvent reelect(NodeId current, DeviceId deviceId) {
         byte [] did = serialize(deviceId);
         byte [] nid = serialize(current);
 
@@ -281,7 +281,7 @@
             evict(backup, did);
             Integer term = terms.get(did);
             terms.put(did, ++term);
-            return new DeviceMastershipEvent(
+            return new MastershipEvent(
                     MASTER_CHANGED, deviceId, (NodeId) deserialize(backup));
         }
     }
@@ -320,7 +320,7 @@
 
         @Override
         protected void onAdd(DeviceId deviceId, NodeId nodeId) {
-            notifyDelegate(new DeviceMastershipEvent(MASTER_CHANGED, deviceId, nodeId));
+            notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
         }
 
         @Override
diff --git a/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedDeviceMastershipStoreTest.java b/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
similarity index 88%
rename from core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedDeviceMastershipStoreTest.java
rename to core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
index d993250..bf1bb38 100644
--- a/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedDeviceMastershipStoreTest.java
+++ b/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
@@ -21,12 +21,12 @@
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.ControllerNode.State;
 import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipEvent;
+import org.onlab.onos.cluster.MastershipEvent.Type;
+import org.onlab.onos.cluster.MastershipStoreDelegate;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipEvent;
-import org.onlab.onos.net.device.DeviceMastershipStoreDelegate;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
-import org.onlab.onos.net.device.DeviceMastershipEvent.Type;
 import org.onlab.onos.store.common.StoreManager;
 import org.onlab.onos.store.common.StoreService;
 import org.onlab.onos.store.common.TestStoreManager;
@@ -40,7 +40,7 @@
 /**
  * Test of the Hazelcast-based distributed MastershipStore implementation.
  */
-public class DistributedDeviceMastershipStoreTest {
+public class DistributedMastershipStoreTest {
 
     private static final DeviceId DID1 = DeviceId.deviceId("of:01");
     private static final DeviceId DID2 = DeviceId.deviceId("of:02");
@@ -54,8 +54,8 @@
     private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
     private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
 
-    private DistributedDeviceMastershipStore dms;
-    private TestDistributedDeviceMastershipStore testStore;
+    private DistributedMastershipStore dms;
+    private TestDistributedMastershipStore testStore;
     private KryoSerializer serializationMgr;
     private StoreManager storeMgr;
 
@@ -77,11 +77,11 @@
 
         serializationMgr = new KryoSerializer();
 
-        dms = new TestDistributedDeviceMastershipStore(storeMgr, serializationMgr);
+        dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
         dms.clusterService = new TestClusterService();
         dms.activate();
 
-        testStore = (TestDistributedDeviceMastershipStore) dms;
+        testStore = (TestDistributedMastershipStore) dms;
     }
 
     @After
@@ -133,7 +133,7 @@
         assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
         assertTrue("wrong state for store:", !dms.terms.isEmpty());
         assertEquals("wrong term",
-                DeviceMastershipTerm.of(N1, 0), dms.getTermFor(DID1));
+                MastershipTerm.of(N1, 0), dms.getTermFor(DID1));
 
         //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
         testStore.setCurrent(CN2);
@@ -143,7 +143,7 @@
         //change term and requestRole() again; should persist
         testStore.increment(DID2);
         assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
-        assertEquals("wrong term", DeviceMastershipTerm.of(N1, 1), dms.getTermFor(DID2));
+        assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
     }
 
     @Test
@@ -155,15 +155,15 @@
 
         //switch over to N2
         assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
-        assertEquals("wrong term", DeviceMastershipTerm.of(N2, 1), dms.getTermFor(DID1));
+        assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
 
         //orphan switch - should be rare case
         assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
-        assertEquals("wrong term", DeviceMastershipTerm.of(N2, 0), dms.getTermFor(DID2));
+        assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
         //disconnect and reconnect - sign of failing re-election or single-instance channel
         testStore.reset(true, false, false);
         dms.setMaster(N2, DID2);
-        assertEquals("wrong term", DeviceMastershipTerm.of(N2, 1), dms.getTermFor(DID2));
+        assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
     }
 
     @Test
@@ -211,9 +211,9 @@
         //shamelessly copy other distributed store tests
         final CountDownLatch addLatch = new CountDownLatch(1);
 
-        DeviceMastershipStoreDelegate checkAdd = new DeviceMastershipStoreDelegate() {
+        MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
             @Override
-            public void notify(DeviceMastershipEvent event) {
+            public void notify(MastershipEvent event) {
                 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
                 assertEquals("wrong subject", DID1, event.subject());
                 assertEquals("wrong subject", N1, event.master());
@@ -227,9 +227,9 @@
         assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
     }
 
-    private class TestDistributedDeviceMastershipStore extends
-            DistributedDeviceMastershipStore {
-        public TestDistributedDeviceMastershipStore(StoreService storeService,
+    private class TestDistributedMastershipStore extends
+            DistributedMastershipStore {
+        public TestDistributedMastershipStore(StoreService storeService,
                 KryoSerializer kryoSerialization) {
             this.storeService = storeService;
             this.serializer = kryoSerialization;
diff --git a/core/store/hz/common/pom.xml b/core/store/hz/common/pom.xml
index 06aa0b7..1d79206 100644
--- a/core/store/hz/common/pom.xml
+++ b/core/store/hz/common/pom.xml
@@ -35,8 +35,8 @@
           <artifactId>hazelcast</artifactId>
         </dependency>
         <dependency>
-          <groupId>de.javakaffee</groupId>
-          <artifactId>kryo-serializers</artifactId>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
         </dependency>
     </dependencies>
 
diff --git a/core/store/hz/net/pom.xml b/core/store/hz/net/pom.xml
index e3bc0e2..177e99e 100644
--- a/core/store/hz/net/pom.xml
+++ b/core/store/hz/net/pom.xml
@@ -23,11 +23,6 @@
         </dependency>
         <dependency>
             <groupId>org.onlab.onos</groupId>
-            <artifactId>onos-core-serializers</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onlab.onos</groupId>
             <artifactId>onos-core-hz-common</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -46,10 +41,6 @@
           <groupId>com.hazelcast</groupId>
           <artifactId>hazelcast</artifactId>
         </dependency>
-        <dependency>
-          <groupId>de.javakaffee</groupId>
-          <artifactId>kryo-serializers</artifactId>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
index 729b01e..b68620a 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
@@ -2,8 +2,8 @@
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.store.ClockProviderService;
 
 // FIXME: Code clone in onos-core-trivial, onos-core-hz-net
@@ -15,6 +15,6 @@
 public class NoOpClockProviderService implements ClockProviderService {
 
     @Override
-    public void setMastershipTerm(DeviceId deviceId, DeviceMastershipTerm term) {
+    public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
     }
 }
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
index 5c706e6..0ca4ae2 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
@@ -1,26 +1,20 @@
 package org.onlab.onos.store.host.impl;
 
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
 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.Annotations;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultHost;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.HostLocation;
 import org.onlab.onos.net.host.HostDescription;
 import org.onlab.onos.net.host.HostEvent;
 import org.onlab.onos.net.host.HostStore;
@@ -33,10 +27,13 @@
 import org.onlab.packet.VlanId;
 import org.slf4j.Logger;
 
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.onlab.onos.net.host.HostEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * TEMPORARY: Manages inventory of end-station hosts using distributed
@@ -46,13 +43,13 @@
 @Component(immediate = true)
 @Service
 public class DistributedHostStore
-extends AbstractStore<HostEvent, HostStoreDelegate>
-implements HostStore {
+        extends AbstractStore<HostEvent, HostStoreDelegate>
+        implements HostStore {
 
     private final Logger log = getLogger(getClass());
 
     // Host inventory
-    private final Map<HostId, Host> hosts = new ConcurrentHashMap<>();
+    private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
 
     // Hosts tracked by their location
     private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
@@ -72,8 +69,8 @@
 
     @Override
     public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
-            HostDescription hostDescription) {
-        Host host = hosts.get(hostId);
+                                        HostDescription hostDescription) {
+        StoredHost host = hosts.get(hostId);
         if (host == null) {
             return createHost(providerId, hostId, hostDescription);
         }
@@ -82,12 +79,12 @@
 
     // creates a new host and sends HOST_ADDED
     private HostEvent createHost(ProviderId providerId, HostId hostId,
-            HostDescription descr) {
-        DefaultHost newhost = new DefaultHost(providerId, hostId,
-                descr.hwAddress(),
-                descr.vlan(),
-                descr.location(),
-                descr.ipAddresses());
+                                 HostDescription descr) {
+        StoredHost newhost = new StoredHost(providerId, hostId,
+                                            descr.hwAddress(),
+                                            descr.vlan(),
+                                            descr.location(),
+                                            ImmutableSet.of(descr.ipAddress()));
         synchronized (this) {
             hosts.put(hostId, newhost);
             locations.put(descr.location(), newhost);
@@ -96,28 +93,24 @@
     }
 
     // checks for type of update to host, sends appropriate event
-    private HostEvent updateHost(ProviderId providerId, Host host,
-            HostDescription descr) {
-        DefaultHost updated;
+    private HostEvent updateHost(ProviderId providerId, StoredHost host,
+                                 HostDescription descr) {
         HostEvent event;
         if (!host.location().equals(descr.location())) {
-            updated = new DefaultHost(providerId, host.id(),
-                    host.mac(),
-                    host.vlan(),
-                    descr.location(),
-                    host.ipAddresses());
-            event = new HostEvent(HOST_MOVED, updated);
+            host.setLocation(descr.location());
+            return new HostEvent(HOST_MOVED, host);
+        }
 
-        } else if (!(host.ipAddresses().equals(descr.ipAddresses()))) {
-            updated = new DefaultHost(providerId, host.id(),
-                    host.mac(),
-                    host.vlan(),
-                    descr.location(),
-                    descr.ipAddresses());
-            event = new HostEvent(HOST_UPDATED, updated);
-        } else {
+        if (host.ipAddresses().contains(descr.ipAddress())) {
             return null;
         }
+
+        Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
+        addresses.add(descr.ipAddress());
+        StoredHost updated = new StoredHost(providerId, host.id(),
+                                            host.mac(), host.vlan(),
+                                            descr.location(), addresses);
+        event = new HostEvent(HOST_UPDATED, updated);
         synchronized (this) {
             hosts.put(host.id(), updated);
             locations.remove(host.location(), host);
@@ -145,7 +138,7 @@
 
     @Override
     public Iterable<Host> getHosts() {
-        return Collections.unmodifiableSet(new HashSet<>(hosts.values()));
+        return ImmutableSet.<Host>copyOf(hosts.values());
     }
 
     @Override
@@ -275,4 +268,35 @@
         return addresses;
     }
 
+    // Auxiliary extension to allow location to mutate.
+    private class StoredHost extends DefaultHost {
+        private HostLocation location;
+
+        /**
+         * Creates an end-station host using the supplied information.
+         *
+         * @param providerId  provider identity
+         * @param id          host identifier
+         * @param mac         host MAC address
+         * @param vlan        host VLAN identifier
+         * @param location    host location
+         * @param ips         host IP addresses
+         * @param annotations optional key/value annotations
+         */
+        public StoredHost(ProviderId providerId, HostId id,
+                          MacAddress mac, VlanId vlan, HostLocation location,
+                          Set<IpPrefix> ips, Annotations... annotations) {
+            super(providerId, id, mac, vlan, location, ips, annotations);
+            this.location = location;
+        }
+
+        void setLocation(HostLocation location) {
+            this.location = location;
+        }
+
+        @Override
+        public HostLocation location() {
+            return location;
+        }
+    }
 }
diff --git a/core/store/serializers/pom.xml b/core/store/serializers/pom.xml
index f222a23..fe0a501 100644
--- a/core/store/serializers/pom.xml
+++ b/core/store/serializers/pom.xml
@@ -26,8 +26,13 @@
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
         <dependency>
-          <groupId>de.javakaffee</groupId>
-          <artifactId>kryo-serializers</artifactId>
+            <groupId>com.esotericsoftware</groupId>
+            <artifactId>kryo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava-testlib</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
index 5676cb6..103e670 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
@@ -7,6 +7,7 @@
 
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultAnnotations;
@@ -23,14 +24,12 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.device.DefaultDeviceDescription;
 import org.onlab.onos.net.device.DefaultPortDescription;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.Timestamp;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.util.KryoPool;
 
-import de.javakaffee.kryoserializers.URISerializer;
-
 public final class KryoPoolUtil {
 
     /**
@@ -63,7 +62,9 @@
                     Port.class,
                     DefaultPortDescription.class,
                     Element.class,
-                    Link.Type.class
+                    Link.Type.class,
+                    Timestamp.class
+
                     )
             .register(URI.class, new URISerializer())
             .register(NodeId.class, new NodeIdSerializer())
@@ -74,7 +75,7 @@
             .register(LinkKey.class, new LinkKeySerializer())
             .register(ConnectPoint.class, new ConnectPointSerializer())
             .register(DefaultLink.class, new DefaultLinkSerializer())
-            .register(DeviceMastershipTerm.class, new MastershipTermSerializer())
+            .register(MastershipTerm.class, new MastershipTermSerializer())
             .register(MastershipRole.class, new MastershipRoleSerializer())
 
             .build();
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java
index 738086e..3920dd6 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializer.java
@@ -1,9 +1,6 @@
 package org.onlab.onos.store.serializers;
 
 import org.onlab.util.KryoPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.nio.ByteBuffer;
 
 /**
@@ -11,10 +8,8 @@
  */
 public class KryoSerializer implements StoreSerializer {
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
     protected KryoPool serializerPool;
 
-
     public KryoSerializer() {
         setupKryoPool();
     }
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
index 0d0a6b0..0ac61a8 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
@@ -1,20 +1,19 @@
 package org.onlab.onos.store.serializers;
 
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
-
 import com.esotericsoftware.kryo.Kryo;
 import com.esotericsoftware.kryo.Serializer;
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
 
 /**
- * Kryo Serializer for {@link org.onlab.onos.net.device.DeviceMastershipTerm}.
+ * Kryo Serializer for {@link org.onlab.onos.cluster.MastershipTerm}.
  */
-public class MastershipTermSerializer extends Serializer<DeviceMastershipTerm> {
+public class MastershipTermSerializer extends Serializer<MastershipTerm> {
 
     /**
-     * Creates {@link DeviceMastershipTerm} serializer instance.
+     * Creates {@link MastershipTerm} serializer instance.
      */
     public MastershipTermSerializer() {
         // non-null, immutable
@@ -22,14 +21,14 @@
     }
 
     @Override
-    public DeviceMastershipTerm read(Kryo kryo, Input input, Class<DeviceMastershipTerm> type) {
+    public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) {
         final NodeId node = new NodeId(input.readString());
         final int term = input.readInt();
-        return DeviceMastershipTerm.of(node, term);
+        return MastershipTerm.of(node, term);
     }
 
     @Override
-    public void write(Kryo kryo, Output output, DeviceMastershipTerm object) {
+    public void write(Kryo kryo, Output output, MastershipTerm object) {
         output.writeString(object.master().toString());
         output.writeInt(object.termNumber());
     }
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/URISerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/URISerializer.java
new file mode 100644
index 0000000..4c2d61e
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/URISerializer.java
@@ -0,0 +1,31 @@
+package org.onlab.onos.store.serializers;
+
+import java.net.URI;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Serializer for {@link URI}.
+ */
+public class URISerializer extends Serializer<URI> {
+
+    /**
+     * Creates {@link URI} serializer instance.
+     */
+    public URISerializer() {
+        super(false);
+    }
+
+    @Override
+    public void write(Kryo kryo, Output output, URI object) {
+        output.writeString(object.toString());
+    }
+
+    @Override
+    public URI read(Kryo kryo, Input input, Class<URI> type) {
+        return URI.create(input.readString());
+    }
+}
diff --git a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java
index ab9e012..d651d56 100644
--- a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java
+++ b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTest.java
@@ -10,6 +10,7 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.Annotations;
 import org.onlab.onos.net.ConnectPoint;
@@ -24,7 +25,6 @@
 import org.onlab.onos.net.MastershipRole;
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.SparseAnnotations;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
@@ -114,7 +114,7 @@
         testSerialized(PID);
         testSerialized(PIDA);
         testSerialized(new NodeId("bar"));
-        testSerialized(DeviceMastershipTerm.of(new NodeId("foo"), 2));
+        testSerialized(MastershipTerm.of(new NodeId("foo"), 2));
         for (MastershipRole role : MastershipRole.values()) {
             testSerialized(role);
         }
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
index 5f3c2ef..ff4b31a 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
@@ -2,8 +2,8 @@
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.store.ClockProviderService;
 
 //FIXME: Code clone in onos-core-trivial, onos-core-hz-net
@@ -15,6 +15,6 @@
 public class NoOpClockProviderService implements ClockProviderService {
 
     @Override
-    public void setMastershipTerm(DeviceId deviceId, DeviceMastershipTerm term) {
+    public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
     }
 }
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
index 92d6a22..67ed050 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
@@ -1,26 +1,20 @@
 package org.onlab.onos.store.trivial.impl;
 
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
-import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
 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.Annotations;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DefaultHost;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.HostLocation;
 import org.onlab.onos.net.host.HostDescription;
 import org.onlab.onos.net.host.HostEvent;
 import org.onlab.onos.net.host.HostStore;
@@ -33,10 +27,13 @@
 import org.onlab.packet.VlanId;
 import org.slf4j.Logger;
 
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.onlab.onos.net.host.HostEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Manages inventory of end-station hosts using trivial in-memory
@@ -51,7 +48,7 @@
     private final Logger log = getLogger(getClass());
 
     // Host inventory
-    private final Map<HostId, Host> hosts = new ConcurrentHashMap<>();
+    private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
 
     // Hosts tracked by their location
     private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
@@ -72,7 +69,7 @@
     @Override
     public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
                                         HostDescription hostDescription) {
-        Host host = hosts.get(hostId);
+        StoredHost host = hosts.get(hostId);
         if (host == null) {
             return createHost(providerId, hostId, hostDescription);
         }
@@ -82,11 +79,11 @@
     // creates a new host and sends HOST_ADDED
     private HostEvent createHost(ProviderId providerId, HostId hostId,
                                  HostDescription descr) {
-        DefaultHost newhost = new DefaultHost(providerId, hostId,
-                                              descr.hwAddress(),
-                                              descr.vlan(),
-                                              descr.location(),
-                                              descr.ipAddresses());
+        StoredHost newhost = new StoredHost(providerId, hostId,
+                                            descr.hwAddress(),
+                                            descr.vlan(),
+                                            descr.location(),
+                                            ImmutableSet.of(descr.ipAddress()));
         synchronized (this) {
             hosts.put(hostId, newhost);
             locations.put(descr.location(), newhost);
@@ -95,28 +92,24 @@
     }
 
     // checks for type of update to host, sends appropriate event
-    private HostEvent updateHost(ProviderId providerId, Host host,
+    private HostEvent updateHost(ProviderId providerId, StoredHost host,
                                  HostDescription descr) {
-        DefaultHost updated;
         HostEvent event;
         if (!host.location().equals(descr.location())) {
-            updated = new DefaultHost(providerId, host.id(),
-                                      host.mac(),
-                                      host.vlan(),
-                                      descr.location(),
-                                      host.ipAddresses());
-            event = new HostEvent(HOST_MOVED, updated);
+            host.setLocation(descr.location());
+            return new HostEvent(HOST_MOVED, host);
+        }
 
-        } else if (!(host.ipAddresses().equals(descr.ipAddresses()))) {
-            updated = new DefaultHost(providerId, host.id(),
-                                      host.mac(),
-                                      host.vlan(),
-                                      descr.location(),
-                                      descr.ipAddresses());
-            event = new HostEvent(HOST_UPDATED, updated);
-        } else {
+        if (host.ipAddresses().contains(descr.ipAddress())) {
             return null;
         }
+
+        Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
+        addresses.add(descr.ipAddress());
+        StoredHost updated = new StoredHost(providerId, host.id(),
+                                            host.mac(), host.vlan(),
+                                            descr.location(), addresses);
+        event = new HostEvent(HOST_UPDATED, updated);
         synchronized (this) {
             hosts.put(host.id(), updated);
             locations.remove(host.location(), host);
@@ -144,7 +137,7 @@
 
     @Override
     public Iterable<Host> getHosts() {
-        return Collections.unmodifiableSet(new HashSet<>(hosts.values()));
+        return ImmutableSet.<Host>copyOf(hosts.values());
     }
 
     @Override
@@ -274,4 +267,35 @@
         return addresses;
     }
 
+    // Auxiliary extension to allow location to mutate.
+    private class StoredHost extends DefaultHost {
+        private HostLocation location;
+
+        /**
+         * Creates an end-station host using the supplied information.
+         *
+         * @param providerId  provider identity
+         * @param id          host identifier
+         * @param mac         host MAC address
+         * @param vlan        host VLAN identifier
+         * @param location    host location
+         * @param ips         host IP addresses
+         * @param annotations optional key/value annotations
+         */
+        public StoredHost(ProviderId providerId, HostId id,
+                          MacAddress mac, VlanId vlan, HostLocation location,
+                          Set<IpPrefix> ips, Annotations... annotations) {
+            super(providerId, id, mac, vlan, location, ips, annotations);
+            this.location = location;
+        }
+
+        void setLocation(HostLocation location) {
+            this.location = location;
+        }
+
+        @Override
+        public HostLocation location() {
+            return location;
+        }
+    }
 }
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java
index 7f88edb..e8096ea 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java
@@ -15,18 +15,18 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipEvent;
+import org.onlab.onos.cluster.MastershipStore;
+import org.onlab.onos.cluster.MastershipStoreDelegate;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.device.DeviceMastershipEvent;
-import org.onlab.onos.net.device.DeviceMastershipStore;
-import org.onlab.onos.net.device.DeviceMastershipStoreDelegate;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 import org.onlab.onos.store.AbstractStore;
 import org.onlab.packet.IpPrefix;
 import org.slf4j.Logger;
 
-import static org.onlab.onos.net.device.DeviceMastershipEvent.Type.*;
+import static org.onlab.onos.cluster.MastershipEvent.Type.*;
 
 /**
  * Manages inventory of controller mastership over devices using
@@ -35,8 +35,8 @@
 @Component(immediate = true)
 @Service
 public class SimpleMastershipStore
-        extends AbstractStore<DeviceMastershipEvent, DeviceMastershipStoreDelegate>
-        implements DeviceMastershipStore {
+        extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
+        implements MastershipStore {
 
     private final Logger log = getLogger(getClass());
 
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public DeviceMastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
+    public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
         MastershipRole role = getRole(nodeId, deviceId);
 
         synchronized (this) {
@@ -86,7 +86,7 @@
             }
         }
 
-        return new DeviceMastershipEvent(MASTER_CHANGED, deviceId, nodeId);
+        return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
     }
 
     @Override
@@ -164,17 +164,17 @@
     }
 
     @Override
-    public DeviceMastershipTerm getTermFor(DeviceId deviceId) {
+    public MastershipTerm getTermFor(DeviceId deviceId) {
         if ((masterMap.get(deviceId) == null) ||
                 (termMap.get(deviceId) == null)) {
             return null;
         }
-        return DeviceMastershipTerm.of(
+        return MastershipTerm.of(
                 masterMap.get(deviceId), termMap.get(deviceId).get());
     }
 
     @Override
-    public DeviceMastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
+    public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
         MastershipRole role = getRole(nodeId, deviceId);
         synchronized (this) {
             switch (role) {
@@ -185,7 +185,7 @@
                     } else {
                         masterMap.put(deviceId, backup);
                         termMap.get(deviceId).incrementAndGet();
-                        return new DeviceMastershipEvent(MASTER_CHANGED, deviceId, backup);
+                        return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
                     }
                 case STANDBY:
                 case NONE:
@@ -215,7 +215,7 @@
     }
 
     @Override
-    public DeviceMastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
+    public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
         return setStandby(nodeId, deviceId);
     }
 
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java
index d3430ca..1e8e5c7 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java
@@ -6,9 +6,9 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.device.DeviceMastershipTerm;
 
 import com.google.common.collect.Sets;
 
@@ -16,7 +16,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.onlab.onos.net.MastershipRole.*;
-import static org.onlab.onos.net.device.DeviceMastershipEvent.Type.*;
+import static org.onlab.onos.cluster.MastershipEvent.Type.*;
 
 /**
  * Test for the simple MastershipStore implementation.
@@ -98,12 +98,12 @@
     @Test
     public void getTermFor() {
         put(DID1, N1, true, true);
-        assertEquals("wrong term", DeviceMastershipTerm.of(N1, 0), sms.getTermFor(DID1));
+        assertEquals("wrong term", MastershipTerm.of(N1, 0), sms.getTermFor(DID1));
 
         //switch to N2 and back - 2 term switches
         sms.setMaster(N2, DID1);
         sms.setMaster(N1, DID1);
-        assertEquals("wrong term", DeviceMastershipTerm.of(N1, 2), sms.getTermFor(DID1));
+        assertEquals("wrong term", MastershipTerm.of(N1, 2), sms.getTermFor(DID1));
     }
 
     @Test
diff --git a/features/features.xml b/features/features.xml
index b240917..c9e6bdd 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -20,10 +20,11 @@
         <bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.0</bundle>
         <bundle>mvn:com.eclipsesource.minimal-json/minimal-json/0.9.1</bundle>
 
-        <bundle>mvn:com.esotericsoftware.kryo/kryo/2.24.0</bundle>
+        <bundle>mvn:com.esotericsoftware/kryo/3.0.0</bundle>
+        <bundle>mvn:com.esotericsoftware/reflectasm/1.10.0</bundle>
+        <bundle>mvn:org.ow2.asm/asm/4.2</bundle>
         <bundle>mvn:com.esotericsoftware/minlog/1.3.0</bundle>
         <bundle>mvn:org.objenesis/objenesis/2.1</bundle>
-        <bundle>mvn:de.javakaffee/kryo-serializers/0.27</bundle>
 
         <bundle>mvn:org.onlab.onos/onlab-nio/1.0.0-SNAPSHOT</bundle>
 
diff --git a/openflow/api/pom.xml b/openflow/api/pom.xml
index afc2faf..4e91328 100644
--- a/openflow/api/pom.xml
+++ b/openflow/api/pom.xml
@@ -30,7 +30,7 @@
             <groupId>org.projectfloodlight</groupId>
             <artifactId>openflowj</artifactId>
             <!-- FIXME once experimenter gets merged to upstream -->
-            <version>0.3.8-optical_experimenter</version>
+            <version>0.3.8-optical_experimenter2</version>
         </dependency>
         <dependency>
             <groupId>io.netty</groupId>
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
index 5be7c69..1a48183 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
@@ -981,13 +981,13 @@
                 // switch was a duplicate-dpid, calling the method below would clear
                 // all state for the original switch (with the same dpid),
                 // which we obviously don't want.
-                log.info("{}:removal called");
+                log.info("{}:removal called", getSwitchInfoString());
                 sw.removeConnectedSwitch();
             } else {
                 // A duplicate was disconnected on this ChannelHandler,
                 // this is the same switch reconnecting, but the original state was
                 // not cleaned up - XXX check liveness of original ChannelHandler
-                log.info("{}:duplicate found");
+                log.info("{}:duplicate found", getSwitchInfoString());
                 duplicateDpidFound = Boolean.FALSE;
             }
         } else {
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java
index 868eb86..640cea8 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java
@@ -57,6 +57,12 @@
             }
         }
 
+        String sw = desc.getSwDesc();
+        if (sw.startsWith("LINC-OE")) {
+            log.debug("Optical Emulator LINC-OE with DPID:{} found..", dpid);
+            return new OFOpticalSwitchImplLINC13(dpid, desc);
+        }
+
         log.warn("DriverManager could not identify switch desc: {}. "
                 + "Assigning AbstractOpenFlowSwich", desc);
         return new AbstractOpenFlowSwitch(dpid, desc) {
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java
new file mode 100644
index 0000000..71a7ab8
--- /dev/null
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java
@@ -0,0 +1,383 @@
+package org.onlab.onos.openflow.drivers.impl;
+
+import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
+import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
+import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
+import org.onlab.onos.openflow.controller.Dpid;
+import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch;
+import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
+import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFMatchV3;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFOxmList;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortOptical;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtype;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtypeBasic;
+import org.projectfloodlight.openflow.types.CircuitSignalID;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U8;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * LINC-OE Optical Emulator switch class.
+ */
+public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
+
+    private final AtomicBoolean driverHandshakeComplete;
+    private long barrierXidToWaitFor = -1;
+
+    private final Logger log =
+            LoggerFactory.getLogger(OFOpticalSwitchImplLINC13.class);
+
+    OFOpticalSwitchImplLINC13(Dpid dpid, OFDescStatsReply desc) {
+        super(dpid);
+        //setAttribute("optical", "true");
+        driverHandshakeComplete = new AtomicBoolean(false);
+        setSwitchDescription(desc);
+    }
+
+    @Override
+    public String toString() {
+        return "OFOpticalSwitchImplLINC13 [" + ((channel != null)
+                ? channel.getRemoteAddress() : "?")
+                + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
+    }
+
+    @Override
+    public void startDriverHandshake() {
+        log.debug("Starting driver handshake for sw {}", getStringId());
+        if (startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeAlreadyStarted();
+        }
+        startDriverHandshakeCalled = true;
+        try {
+            sendHandshakeOFExperimenterPortDescRequest();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public boolean isDriverHandshakeComplete() {
+        if (!startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeNotStarted();
+        }
+        return driverHandshakeComplete.get();
+    }
+
+    @Override
+    public void processDriverHandshakeMessage(OFMessage m) {
+        if (!startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeNotStarted();
+        }
+        if (driverHandshakeComplete.get()) {
+            throw new SwitchDriverSubHandshakeCompleted(m);
+        }
+
+        switch (m.getType()) {
+            case BARRIER_REPLY:
+                if (m.getXid() == barrierXidToWaitFor) {
+                    log.debug("LINC-OE Received barrier response");
+                }
+                break;
+            case ERROR:
+                log.error("Switch {} Error {}", getStringId(), (OFErrorMsg) m);
+                break;
+            case FEATURES_REPLY:
+                break;
+            case FLOW_REMOVED:
+                break;
+            case GET_ASYNC_REPLY:
+                break;
+            case PACKET_IN:
+                break;
+            case PORT_STATUS:
+                break;
+            case QUEUE_GET_CONFIG_REPLY:
+                break;
+            case ROLE_REPLY:
+                break;
+            case STATS_REPLY:
+                log.debug("LINC-OE : Received stats reply message {}", m);
+                processHandshakeOFExperimenterPortDescRequest(
+                        (OFCircuitPortsReply) m);
+                driverHandshakeComplete.set(true);
+               /* try {
+                    testMA();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }*/
+                break;
+            default:
+                log.debug("Received message {} during switch-driver " +
+                                  "subhandshake " + "from switch {} ... " +
+                                  "Ignoring message", m,
+                          getStringId());
+
+        }
+    }
+
+
+    private void processHandshakeOFExperimenterPortDescRequest(
+            OFCircuitPortsReply sr) {
+        Collection<OFPortOptical> entries = sr.getEntries();
+        List<OFPortDesc> ofPortDescList = new ArrayList<>(entries.size());
+        for (OFPortOptical entry : entries) {
+            ofPortDescList.add(factory().buildPortDesc().
+                    setPortNo(entry.getPortNo())
+                                           .setConfig(entry.getConfig())
+                                           .setState(entry.getState())
+                                           .setHwAddr(entry.getHwAddr())
+                                           .setName(entry.getName())
+                                           .build());
+        }
+        setPortDescReply(factory().buildPortDescStatsReply().
+                setEntries(ofPortDescList).build());
+    }
+
+
+    private void sendHandshakeOFExperimenterPortDescRequest() throws
+            IOException {
+        // send multi part message for port description for optical switches
+        OFCircuitPortsRequest circuitPortsRequest = factory()
+                .buildCircuitPortsRequest().setXid(getNextTransactionId())
+                .build();
+        log.debug("LINC-OE : Sending experimented circuit port stats " +
+                          "message " +
+                          "{}",
+                  circuitPortsRequest.toString());
+        channel.write(Collections.singletonList(circuitPortsRequest));
+    }
+
+
+
+    //todo for testing
+    public static final U8 SIGNAL_TYPE = U8.of((short) 1);
+    private void testMA() throws IOException {
+        log.debug("LINC OE *** Testing MA ");
+        short lambda = 100;
+        if (getId() == 0x0000ffffffffff02L) {
+            final int inport = 10;
+            final int outport = 20;
+            //Circuit signal id
+            CircuitSignalID sigID = getSignalID(lambda);
+
+            OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
+            OFOxmOchSigtype fieldSigType = factory()
+                    .oxms()
+                    .ochSigtype(SIGNAL_TYPE);
+
+            OFOxmOchSigidBasic ofOxmOchSigidBasic =
+                    factory().oxms().ochSigidBasic(sigID);
+
+            OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic =
+                    factory().oxms().ochSigtypeBasic(SIGNAL_TYPE);
+
+            //Match Port
+            OFOxmInPort fieldPort = factory().oxms()
+                                                .inPort(OFPort.of(inport));
+            OFMatchV3 matchPort =
+                    factory()
+                            .buildMatchV3().
+                            setOxmList(OFOxmList.of(fieldPort,
+                                                    fieldSigType,
+                                                    fieldSigIDMatch)).build();
+
+
+            // Set Action outport ,sigType and sigID
+            List<OFAction> actionList = new ArrayList<>();
+            OFAction actionOutPort =
+                    factory().actions().output(OFPort.of(outport),
+                                                  Short.MAX_VALUE);
+
+            OFActionCircuit actionCircuit = factory()
+                    .actions()
+                    .circuit(ofOxmOchSigidBasic);
+            OFActionCircuit setActionSigType = factory()
+                    .actions()
+                    .circuit(ofOxmOchSigtypeBasic);
+
+            actionList.add(actionOutPort);
+            actionList.add(setActionSigType);
+            actionList.add(actionCircuit);
+
+            OFInstruction instructionAction =
+                    factory().instructions().buildApplyActions()
+                                .setActions(actionList)
+                                .build();
+            List<OFInstruction> instructions =
+                    Collections.singletonList(instructionAction);
+
+            OFMessage opticalFlowEntry =
+                    factory().buildFlowAdd()
+                                .setMatch(matchPort)
+                                .setInstructions(instructions)
+                                .setXid(getNextTransactionId())
+                                .build();
+            log.debug("Adding optical flow in sw {}", getStringId());
+            List<OFMessage> msglist = new ArrayList<>(1);
+            msglist.add(opticalFlowEntry);
+            write(msglist);
+        } else if (getId() == 0x0000ffffffffff03L) {
+            final int inport = 21;
+            final int outport = 22;
+            //Circuit signal id
+            CircuitSignalID sigID = getSignalID(lambda);
+
+            OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
+            OFOxmOchSigtype fieldSigType = factory()
+                    .oxms()
+                    .ochSigtype(SIGNAL_TYPE);
+
+            OFOxmOchSigidBasic ofOxmOchSigidBasic =
+                    factory().oxms().ochSigidBasic(sigID);
+
+            OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic =
+                    factory().oxms().ochSigtypeBasic(SIGNAL_TYPE);
+
+            //Match Port,SigType,SigID
+            OFOxmInPort fieldPort = factory()
+                    .oxms()
+                    .inPort(OFPort.of(inport));
+            OFMatchV3 matchPort = factory()
+                    .buildMatchV3()
+                    .setOxmList(OFOxmList.of(fieldPort,
+                                             fieldSigType,
+                                             fieldSigIDMatch))
+                    .build();
+
+            // Set Action outport ,SigType, sigID
+            List<OFAction> actionList = new ArrayList<>();
+            OFAction actionOutPort =
+                    factory().actions().output(OFPort.of(outport),
+                                                  Short.MAX_VALUE);
+
+            OFActionCircuit setActionSigType = factory()
+                    .actions()
+                    .circuit(ofOxmOchSigtypeBasic);
+            OFActionCircuit actionCircuit = factory()
+                    .actions()
+                    .circuit(ofOxmOchSigidBasic);
+
+
+            actionList.add(actionOutPort);
+            actionList.add(setActionSigType);
+            actionList.add(actionCircuit);
+
+            OFInstruction instructionAction =
+                    factory().instructions().buildApplyActions()
+                                .setActions(actionList)
+                                .build();
+            List<OFInstruction> instructions =
+                    Collections.singletonList(instructionAction);
+
+            OFMessage opticalFlowEntry =
+                    factory().buildFlowAdd()
+                                 .setMatch(matchPort)
+                                 .setInstructions(instructions)
+                                 .setXid(getNextTransactionId())
+                                 .build();
+            log.debug("Adding optical flow in sw {}", getStringId());
+            List<OFMessage> msglist = new ArrayList<>(1);
+            msglist.add(opticalFlowEntry);
+            write(msglist);
+
+        } else if (getId() == 0x0000ffffffffff04L) {
+            final int inport = 23;
+            final int outport = 11;
+            //Circuit signal id
+            CircuitSignalID sigID = getSignalID(lambda);
+
+            OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
+            OFOxmOchSigtype fieldSigType = factory()
+                    .oxms()
+                    .ochSigtype(SIGNAL_TYPE);
+
+
+            //Match Port, sig type and sig id
+            OFOxmInPort fieldPort = factory()
+                    .oxms()
+                    .inPort(OFPort.of(inport));
+            OFMatchV3 matchPort =
+                    factory().buildMatchV3()
+                                .setOxmList(OFOxmList.of(fieldPort,
+                                                         fieldSigType,
+                                                         fieldSigIDMatch))
+                                .build();
+
+            // Set Action outport
+            List<OFAction> actionList = new ArrayList<>();
+            OFAction actionOutPort =
+                    factory().actions().output(OFPort.of(outport),
+                                                  Short.MAX_VALUE);
+
+            actionList.add(actionOutPort);
+
+            OFInstruction instructionAction =
+                    factory().instructions().buildApplyActions()
+                                .setActions(actionList)
+                                .build();
+            List<OFInstruction> instructions =
+                    Collections.singletonList(instructionAction);
+
+            OFMessage opticalFlowEntry =
+                    factory().buildFlowAdd()
+                                 .setMatch(matchPort)
+                                 .setInstructions(instructions)
+                                 .setXid(getNextTransactionId())
+                                 .build();
+            log.debug("Adding optical flow in sw {}", getStringId());
+            List<OFMessage> msglist = new ArrayList<>(1);
+            msglist.add(opticalFlowEntry);
+            write(msglist);
+        }
+
+    }
+
+    // Todo remove - for testing purpose only
+    private static CircuitSignalID getSignalID(short lambda) {
+        byte myGrid = 1;
+        byte myCs = 2;
+        short myCn = lambda;
+        short mySw = 1;
+
+        CircuitSignalID signalID = new CircuitSignalID(myGrid,
+                                                       myCs,
+                                                       myCn,
+                                                       mySw);
+        return signalID;
+    }
+
+    @Override
+    public void write(OFMessage msg) {
+        this.channel.write(msg);
+    }
+
+    @Override
+    public void write(List<OFMessage> msgs) {
+        this.channel.write(msgs);
+    }
+
+    @Override
+    public Boolean supportNxRole() {
+        return false;
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index ad4ddcb..4290769 100644
--- a/pom.xml
+++ b/pom.xml
@@ -193,9 +193,20 @@
                 <version>0.9.1</version>
             </dependency>
             <dependency>
-              <groupId>com.esotericsoftware.kryo</groupId>
-              <artifactId>kryo</artifactId>
-              <version>2.24.0</version>
+                <groupId>com.esotericsoftware</groupId>
+                <artifactId>kryo</artifactId>
+                <version>3.0.0</version>
+            </dependency>
+            <dependency>
+                <groupId>com.esotericsoftware</groupId>
+                <artifactId>reflectasm</artifactId>
+                <version>1.10.0</version>
+                <type>bundle</type>
+            </dependency>
+            <dependency>
+                <groupId>org.ow2.asm</groupId>
+                <artifactId>asm</artifactId>
+                <version>4.2</version>
             </dependency>
             <dependency>
                 <groupId>com.esotericsoftware</groupId>
@@ -207,11 +218,6 @@
                 <artifactId>objenesis</artifactId>
                 <version>2.1</version>
             </dependency>
-            <dependency>
-              <groupId>de.javakaffee</groupId>
-              <artifactId>kryo-serializers</artifactId>
-              <version>0.27</version>
-            </dependency>
 
             <!-- ONOS related -->
             <dependency>
@@ -284,6 +290,10 @@
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
             <artifactId>slf4j-jdk14</artifactId>
         </dependency>
     </dependencies>
@@ -434,9 +444,6 @@
                 <version>3.2</version>
                 <configuration>
                     <excludes>
-                        <exclude>**/datastore/serializers/**</exclude>
-                        <exclude>**/edu/stanford/**</exclude>
-                        <exclude>**/net/floodlightcontroller/**</exclude>
                     </excludes>
                     <rulesets>
                         <ruleset>onos/pmd.xml</ruleset>
@@ -493,7 +500,7 @@
                         <group>
                             <title>Core Subsystems</title>
                             <packages>
-                                org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl
+                                org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl
                             </packages>
                         </group>
                         <group>
@@ -518,7 +525,7 @@
                         <group>
                             <title>Sample Applications</title>
                             <packages>
-                                org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.foo
+                                org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo
                             </packages>
                         </group>
                     </groups>
@@ -545,9 +552,6 @@
                 <version>3.2</version>
                 <configuration>
                     <excludes>
-                        <exclude>**/datastore/serializers/**</exclude>
-                        <exclude>**/edu/stanford/**</exclude>
-                        <exclude>**/net/floodlightcontroller/**</exclude>
                     </excludes>
                     <rulesets>
                         <ruleset>onos/pmd.xml</ruleset>
diff --git a/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java b/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
index 4f5bb81..45a7bd8 100644
--- a/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
+++ b/providers/openflow/host/src/main/java/org/onlab/onos/provider/of/host/impl/OpenFlowHostProvider.java
@@ -1,12 +1,5 @@
 package org.onlab.onos.provider.of.host.impl;
 
-import static com.google.common.collect.Sets.newHashSet;
-import static org.onlab.onos.net.DeviceId.deviceId;
-import static org.onlab.onos.net.PortNumber.portNumber;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Set;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -36,6 +29,10 @@
 import org.onlab.packet.VlanId;
 import org.slf4j.Logger;
 
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.PortNumber.portNumber;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Provider which uses an OpenFlow controller to detect network
  * end-station hosts.
@@ -58,6 +55,8 @@
 
     private final InternalHostProvider listener = new InternalHostProvider();
 
+    private boolean ipLearn = true;
+
     /**
      * Creates an OpenFlow host provider.
      */
@@ -69,7 +68,6 @@
     public void activate() {
         providerService = providerRegistry.register(this);
         controller.addPacketListener(10, listener);
-
         log.info("Started");
     }
 
@@ -78,7 +76,6 @@
         providerRegistry.unregister(this);
         controller.removePacketListener(listener);
         providerService = null;
-
         log.info("Stopped");
     }
 
@@ -95,33 +92,33 @@
 
             VlanId vlan = VlanId.vlanId(eth.getVlanID());
             ConnectPoint heardOn = new ConnectPoint(deviceId(Dpid.uri(pktCtx.dpid())),
-                    portNumber(pktCtx.inPort()));
+                                                    portNumber(pktCtx.inPort()));
 
-         // If this is not an edge port, bail out.
+            // If this is not an edge port, bail out.
             Topology topology = topologyService.currentTopology();
             if (topologyService.isInfrastructure(topology, heardOn)) {
                 return;
             }
 
             HostLocation hloc = new HostLocation(deviceId(Dpid.uri(pktCtx.dpid())),
-                    portNumber(pktCtx.inPort()),
-                    System.currentTimeMillis());
+                                                 portNumber(pktCtx.inPort()),
+                                                 System.currentTimeMillis());
+
             HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
+
             // Potentially a new or moved host
             if (eth.getEtherType() == Ethernet.TYPE_ARP) {
-
-
                 ARP arp = (ARP) eth.getPayload();
-                Set<IpPrefix> ips = newHashSet(IpPrefix.valueOf(arp.getSenderProtocolAddress()));
+                IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress());
                 HostDescription hdescr =
-                        new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ips);
+                        new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
                 providerService.hostDetected(hid, hdescr);
 
-            } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
-                IPv4 ip = (IPv4) eth.getPayload();
-                Set<IpPrefix> ips = newHashSet(IpPrefix.valueOf(ip.getSourceAddress()));
+            } else if (ipLearn && eth.getEtherType() == Ethernet.TYPE_IPV4) {
+                IPv4 pip = (IPv4) eth.getPayload();
+                IpPrefix ip = IpPrefix.valueOf(pip.getSourceAddress());
                 HostDescription hdescr =
-                        new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ips);
+                        new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
                 providerService.hostDetected(hid, hdescr);
 
             }
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index a17ae2a..b4fbc4d 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -6,7 +6,7 @@
 export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
 
 # Setup some environmental context for developers
-export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
+export JAVA_HOME=${JAVA_HOME:-$(/usr/libexec/java_home -v 1.7)}
 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2}
 export KARAF=${KARAF:-~/Applications/apache-karaf-3.0.1}
 export KARAF_LOG=$KARAF/data/log/karaf.log
diff --git a/tools/dev/bin/onos-build-selective-hook b/tools/dev/bin/onos-build-selective-hook
index 0b39713..eb4e482 100755
--- a/tools/dev/bin/onos-build-selective-hook
+++ b/tools/dev/bin/onos-build-selective-hook
@@ -1,3 +1,4 @@
+#!/bin/bash
 #------------------------------------------------------------------------------
 # Echoes project-level directory if a Java file within is newer than the
 # target directory.
diff --git a/tools/package/bin/onos-jpenable b/tools/package/bin/onos-jpenable
new file mode 100755
index 0000000..7c69602
--- /dev/null
+++ b/tools/package/bin/onos-jpenable
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+kpid=$(ps -ef | grep karaf.main.Main | grep -v grep | cut -c10-15 | tr -d ' ')
+
+[ -z "$kpid" ] && echo "No ONOS!" && exit 1
+
+/opt/jprofiler8/bin/jpenable --gui --port=8849 --pid=$kpid
diff --git a/tools/test/cells/cbench b/tools/test/cells/cbench
new file mode 100644
index 0000000..692bb53
--- /dev/null
+++ b/tools/test/cells/cbench
@@ -0,0 +1,7 @@
+# Local VirtualBox-based single ONOS instance & ONOS mininet box
+
+export ONOS_NIC=192.168.56.*
+export OC1="192.168.56.103"
+export OCN="192.168.56.103"
+
+export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd"
diff --git a/utils/misc/pom.xml b/utils/misc/pom.xml
index bd3cc08..5040fb0 100644
--- a/utils/misc/pom.xml
+++ b/utils/misc/pom.xml
@@ -44,18 +44,10 @@
             <artifactId>minimal-json</artifactId>
         </dependency>
         <dependency>
-          <groupId>com.esotericsoftware.kryo</groupId>
+          <groupId>com.esotericsoftware</groupId>
           <artifactId>kryo</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.esotericsoftware</groupId>
-            <artifactId>minlog</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.objenesis</groupId>
-            <artifactId>objenesis</artifactId>
-        </dependency>
-        <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-core</artifactId>
             <version>3.1.0</version>
diff --git a/utils/misc/src/main/java/org/onlab/packet/MacAddress.java b/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
index 814660b..08aa6e3 100644
--- a/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
@@ -22,11 +22,12 @@
  *
  */
 public class MacAddress {
-    public static final byte[] ZERO_MAC_ADDRESS =
-            MacAddress.valueOf("00:00:00:00:00:00").getAddress();
 
-    public static final byte[] BROADCAST_MAC =
-            MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
+    public static final MacAddress ZERO = valueOf("00:00:00:00:00:00");
+    public static final MacAddress BROADCAST = valueOf("ff:ff:ff:ff:ff:ff");
+
+    public static final byte[] ZERO_MAC_ADDRESS = ZERO.getAddress();
+    public static final byte[] BROADCAST_MAC = BROADCAST.getAddress();
 
     public static final int MAC_ADDRESS_LENGTH = 6;
     private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH];
diff --git a/utils/misc/src/main/java/org/onlab/packet/VlanId.java b/utils/misc/src/main/java/org/onlab/packet/VlanId.java
index 60daec7..266a67c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/VlanId.java
+++ b/utils/misc/src/main/java/org/onlab/packet/VlanId.java
@@ -3,12 +3,15 @@
 /**
  * Representation of a VLAN ID.
  */
-// FIXME: This will end-up looking like a constant; we should name it 'VlanId', 'IpAddress', 'MacAddress'.
 public class VlanId {
 
     private final short value;
+
     // Based on convention used elsewhere? Check and change if needed
     public static final short UNTAGGED = (short) 0xffff;
+
+    public static final VlanId NONE = VlanId.vlanId(UNTAGGED);
+
     // A VLAN ID is actually 12 bits of a VLAN tag.
     public static final short MAX_VLAN = 4095;
 
diff --git a/utils/netty/pom.xml b/utils/netty/pom.xml
index a980d1d..effbc3b 100644
--- a/utils/netty/pom.xml
+++ b/utils/netty/pom.xml
@@ -32,10 +32,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>de.javakaffee</groupId>
-            <artifactId>kryo-serializers</artifactId>
-        </dependency>
-        <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
         </dependency>