Merge remote-tracking branch 'origin/master'
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java
index 2a5e62e..15811fb 100644
--- a/core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipEvent.java
@@ -8,6 +8,8 @@
  */
 public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceId> {
 
+    //do we worry about explicitly setting slaves/equals? probably not,
+    //to keep it simple
     NodeId master;
 
     /**
@@ -28,7 +30,7 @@
      * @param device event device subject
      * @param master master ID subject
      */
-    protected MastershipEvent(Type type, DeviceId device, NodeId master) {
+    public MastershipEvent(Type type, DeviceId device, NodeId master) {
         super(type, device);
         this.master = master;
     }
@@ -42,7 +44,7 @@
      * @param master master ID subject
      * @param time   occurrence time
      */
-    protected MastershipEvent(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/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 492f0d4..ab27dc3 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
@@ -65,7 +65,10 @@
         checkNotNull(nodeId, NODE_ID_NULL);
         checkNotNull(deviceId, DEVICE_ID_NULL);
         checkNotNull(role, ROLE_NULL);
-        store.setRole(nodeId, deviceId, role);
+        MastershipEvent event = store.setRole(nodeId, deviceId, role);
+        if (event != null) {
+            post(event);
+        }
     }
 
     @Override
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 8dcba64..179e214 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
@@ -6,6 +6,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipService;
 import org.onlab.onos.event.AbstractListenerRegistry;
 import org.onlab.onos.event.EventDeliveryService;
 import org.onlab.onos.net.Device;
@@ -29,6 +30,7 @@
 
 import java.util.List;
 
+import static org.onlab.onos.net.device.DeviceEvent.Type.*;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -58,6 +60,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
     @Activate
     public void activate() {
         eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
@@ -171,6 +176,10 @@
             // If there was a change of any kind, trigger role selection process.
             if (event != null) {
                 log.info("Device {} connected", deviceId);
+                if (event.type().equals(DEVICE_ADDED)) {
+                    MastershipRole role = mastershipService.requestRoleFor(deviceId);
+                    store.setRole(deviceId, role);
+                }
                 Device device = event.subject();
                 provider().roleChanged(device, store.getRole(device.id()));
                 post(event);
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 209bdd0..2352a96 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
@@ -3,6 +3,9 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.onos.cluster.MastershipListener;
+import org.onlab.onos.cluster.MastershipService;
+import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.event.Event;
 import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
@@ -25,9 +28,12 @@
 import org.onlab.onos.event.impl.TestEventDispatcher;
 import org.onlab.onos.net.trivial.impl.SimpleDeviceStore;
 
+import com.google.common.collect.Sets;
+
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import static org.junit.Assert.*;
 import static org.onlab.onos.net.Device.Type.SWITCH;
@@ -69,6 +75,7 @@
         registry = mgr;
         mgr.store = new SimpleDeviceStore();
         mgr.eventDispatcher = new TestEventDispatcher();
+        mgr.mastershipService = new TestMastershipService();
         mgr.activate();
 
         service.addListener(listener);
@@ -252,4 +259,31 @@
         }
     }
 
+    private static class TestMastershipService implements MastershipService {
+
+        @Override
+        public NodeId getMasterFor(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public Set<DeviceId> getDevicesOf(NodeId nodeId) {
+            return Sets.newHashSet(DID1, DID2);
+        }
+
+        @Override
+        public MastershipRole requestRoleFor(DeviceId deviceId) {
+            return MastershipRole.MASTER;
+        }
+
+        @Override
+        public void addListener(MastershipListener listener) {
+        }
+
+        @Override
+        public void removeListener(MastershipListener listener) {
+        }
+
+    }
+
 }
diff --git a/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java
index 85246e9..332634f 100644
--- a/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java
@@ -1,12 +1,17 @@
 package org.onlab.onos.net.device.impl;
 
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 import com.hazelcast.config.Config;
 import com.hazelcast.core.Hazelcast;
 import com.hazelcast.core.HazelcastInstance;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.onos.cluster.MastershipListener;
+import org.onlab.onos.cluster.MastershipService;
+import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.event.Event;
 import org.onlab.onos.event.impl.TestEventDispatcher;
 import org.onlab.onos.net.Device;
@@ -34,6 +39,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 import static org.junit.Assert.*;
@@ -98,6 +104,7 @@
         dstore.activate();
         mgr.store = dstore;
         mgr.eventDispatcher = new TestEventDispatcher();
+        mgr.mastershipService = new TestMastershipService();
         mgr.activate();
 
         service.addListener(listener);
@@ -302,4 +309,32 @@
             setupKryoPool();
         }
     }
+
+    private static class TestMastershipService implements MastershipService {
+
+        @Override
+        public NodeId getMasterFor(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public Set<DeviceId> getDevicesOf(NodeId nodeId) {
+            return Sets.newHashSet(DID1, DID2);
+        }
+
+        @Override
+        public MastershipRole requestRoleFor(DeviceId deviceId) {
+            return MastershipRole.MASTER;
+        }
+
+        @Override
+        public void addListener(MastershipListener listener) {
+        }
+
+        @Override
+        public void removeListener(MastershipListener listener) {
+        }
+
+    }
+
 }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java
index 8e42ffb..e416756 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java
@@ -98,7 +98,7 @@
             availableDevices.add(deviceId);
 
             // For now claim the device as a master automatically.
-            roles.put(deviceId, MastershipRole.MASTER);
+            // roles.put(deviceId, MastershipRole.MASTER);
         }
         return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null);
     }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java
new file mode 100644
index 0000000..371a257
--- /dev/null
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java
@@ -0,0 +1,93 @@
+package org.onlab.onos.net.trivial.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+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.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.NodeId;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.packet.IpPrefix;
+import org.slf4j.Logger;
+
+import static org.onlab.onos.cluster.MastershipEvent.Type.*;
+
+/**
+ * Manages inventory of controller mastership over devices using
+ * trivial in-memory structures implementation.
+ */
+@Component(immediate = true)
+@Service
+public class SimpleMastershipStore implements MastershipStore {
+
+    public static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1");
+
+    private final Logger log = getLogger(getClass());
+
+    private ControllerNode instance;
+
+    protected final ConcurrentMap<DeviceId, MastershipRole> roleMap =
+            new ConcurrentHashMap<DeviceId, MastershipRole>();
+
+    @Activate
+    public void activate() {
+        instance = new DefaultControllerNode(new NodeId("local"), LOCALHOST);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public MastershipEvent setRole(NodeId nodeId, DeviceId deviceId,
+            MastershipRole role) {
+        if (roleMap.get(deviceId) == null) {
+            return null;
+        }
+        roleMap.put(deviceId, role);
+        return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
+    }
+
+    @Override
+    public MastershipEvent addOrUpdateDevice(NodeId instance,
+            DeviceId deviceId, MastershipRole role) {
+        //TODO refine when we do listeners
+        roleMap.put(deviceId, role);
+        return null;
+    }
+
+    @Override
+    public NodeId getMaster(DeviceId deviceId) {
+        return instance.id();
+    }
+
+    @Override
+    public Set<DeviceId> getDevices(NodeId nodeId) {
+        return Collections.unmodifiableSet(roleMap.keySet());
+    }
+
+    @Override
+    public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
+        MastershipRole role = roleMap.get(deviceId);
+        if (role == null) {
+            //say MASTER. If clustered, we'd figure out if anyone's got dibs here.
+            role = MastershipRole.MASTER;
+            roleMap.put(deviceId, role);
+        }
+        return role;
+    }
+
+}