fixes in mastership reelection for single-node failure

Change-Id: Iedcab52bb156643464a97435fcc39c5db7393976
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 125745b..ba3e616 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
@@ -4,6 +4,7 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -14,6 +15,7 @@
 import org.onlab.onos.cluster.ClusterEvent;
 import org.onlab.onos.cluster.ClusterEventListener;
 import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.MastershipAdminService;
 import org.onlab.onos.cluster.MastershipEvent;
 import org.onlab.onos.cluster.MastershipListener;
@@ -164,21 +166,68 @@
     //callback for reacting to cluster events
     private class InternalClusterEventListener implements ClusterEventListener {
 
+        // A notion of a local maximum cluster size, used to tie-break.
+        // Think of a better way to do this.
+        private AtomicInteger clusterSize;
+
+        InternalClusterEventListener() {
+            clusterSize = new AtomicInteger(0);
+        }
+
         @Override
         public void event(ClusterEvent event) {
             switch (event.type()) {
                 //FIXME: worry about addition when the time comes
                 case INSTANCE_ADDED:
                 case INSTANCE_ACTIVATED:
-                     break;
+                    clusterSize.incrementAndGet();
+                    log.info("instance {} added/activated", event.subject());
+                    break;
                 case INSTANCE_REMOVED:
                 case INSTANCE_DEACTIVATED:
+                    ControllerNode node = event.subject();
+
+                    if (node.equals(clusterService.getLocalNode())) {
+                        //If we are in smaller cluster, relinquish and return
+                        for (DeviceId device : getDevicesOf(node.id())) {
+                            if (!isInMajority()) {
+                                //own DeviceManager should catch event and tell switch
+                                store.relinquishRole(node.id(), device);
+                            }
+                        }
+                        log.info("broke off from cluster, relinquished devices");
+                        break;
+                    }
+
+                    // if we are the larger one and the removed node(s) are brain dead,
+                    // force relinquish on behalf of disabled node.
+                    // check network channel to do this?
+                    for (DeviceId device : getDevicesOf(node.id())) {
+                        //some things to check:
+                        // 1. we didn't break off as well while we're at it
+                        // 2. others don't pile in and try too - maybe a lock
+                        if (isInMajority()) {
+                            store.relinquishRole(node.id(), device);
+                        }
+                    }
+                    clusterSize.decrementAndGet();
+                    log.info("instance {} removed/deactivated", event.subject());
                     break;
                 default:
                     log.warn("unknown cluster event {}", event);
             }
         }
 
+        private boolean isInMajority() {
+            if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
+                return true;
+            }
+            //else {
+                //FIXME: break tie for equal-sized clusters, can we use hz's functions?
+            // }
+            return false;
+        }
+
     }
 
     public class InternalDelegate implements MastershipStoreDelegate {