handle devices agreed to be inactive during master re-election

Change-Id: Ia193d7210a8319f04ce957f2bd4a0479b88d15a8
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceService.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceService.java
index 8364935..54b9d72 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DeviceService.java
@@ -42,6 +42,7 @@
      * @param deviceId device identifier
      * @return designated mastership role
      */
+    //XXX do we want this method here when MastershipService already does?
     MastershipRole getRole(DeviceId deviceId);
 
 
diff --git a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
index 6a96c01..04833e6 100644
--- a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
+++ b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
@@ -10,6 +10,7 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.cluster.ClusterService;
 import org.onlab.onos.cluster.MastershipEvent;
@@ -19,6 +20,7 @@
 import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.device.DeviceService;
 import org.onlab.onos.store.common.AbstractHazelcastStore;
 
 import com.google.common.collect.ImmutableSet;
@@ -51,6 +53,10 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ClusterService clusterService;
 
+    //FIXME: need to guarantee that this will be met, sans circular dependencies
+    @Reference(policy = ReferencePolicy.DYNAMIC)
+    protected DeviceService deviceService;
+
     @Override
     @Activate
     public void activate() {
@@ -230,9 +236,11 @@
 
     //helper for "re-electing" a new master for a given device
     private NodeId reelect(NodeId current, DeviceId deviceId) {
+
         for (byte [] node : backups.keySet()) {
             NodeId nid = deserialize(node);
-            if (!current.equals(nid)) {
+            //if a device dies we shouldn't pick another master for it.
+            if (!current.equals(nid) && (deviceService.isAvailable(deviceId))) {
                 return nid;
             }
         }
diff --git a/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java b/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
index b2c0e2e..c3efdfe 100644
--- a/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
+++ b/core/store/hz/cluster/src/test/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStoreTest.java
@@ -5,6 +5,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.onlab.onos.net.MastershipRole.*;
 
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -25,7 +26,13 @@
 import org.onlab.onos.cluster.MastershipStoreDelegate;
 import org.onlab.onos.cluster.MastershipTerm;
 import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.device.DeviceListener;
+import org.onlab.onos.net.device.DeviceService;
 import org.onlab.onos.store.common.StoreManager;
 import org.onlab.onos.store.common.StoreService;
 import org.onlab.onos.store.common.TestStoreManager;
@@ -80,6 +87,7 @@
 
         dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
         dms.clusterService = new TestClusterService();
+        dms.deviceService = new TestDeviceService();
         dms.activate();
 
         testStore = (TestDistributedMastershipStore) dms;
@@ -179,8 +187,11 @@
         assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
         //no backup, no new MASTER/event
         assertNull("wrong event:", dms.unsetMaster(N1, DID1));
-        //add backup CN2, get it elected MASTER
+
         dms.requestRole(DID1);
+        ((TestDeviceService) dms.deviceService).active.add(DID1);
+
+        //add backup CN2, get it elected MASTER by relinquishing
         testStore.setCurrent(CN2);
         dms.requestRole(DID1);
         assertEquals("wrong event:", Type.MASTER_CHANGED, dms.unsetMaster(N1, DID1).type());
@@ -193,6 +204,12 @@
         //NONE - nothing happens
         assertNull("wrong event:", dms.unsetMaster(N1, DID2));
         assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
+
+        //for a device that turned off (not active) - status to NONE
+        ((TestDeviceService) dms.deviceService).active.clear();
+        assertNull("extraneous event:", dms.unsetMaster(N2, DID1));
+        assertEquals("wrong role", NONE, dms.getRole(N2, DID1));
+
     }
 
     @Ignore("Ignore until Delegate spec. is clear.")
@@ -299,4 +316,53 @@
         }
 
     }
+
+    private class TestDeviceService implements DeviceService {
+
+        Set<DeviceId> active = Sets.newHashSet();
+
+        @Override
+        public int getDeviceCount() {
+            return 0;
+        }
+
+        @Override
+        public Iterable<Device> getDevices() {
+            return null;
+        }
+
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public MastershipRole getRole(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public List<Port> getPorts(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+            return null;
+        }
+
+        @Override
+        public boolean isAvailable(DeviceId deviceId) {
+            return active.contains(deviceId);
+        }
+
+        @Override
+        public void addListener(DeviceListener listener) {
+        }
+
+        @Override
+        public void removeListener(DeviceListener listener) {
+        }
+
+    }
 }