Fixing various issues and re-tuning.

Change-Id: I8822fcf77cfa507788241c5bda98ef4741b284b4
diff --git a/cli/src/main/java/org/onlab/onos/cli/BalanceMastersCommand.java b/cli/src/main/java/org/onlab/onos/cli/BalanceMastersCommand.java
index a3f3c23..77d0c16 100644
--- a/cli/src/main/java/org/onlab/onos/cli/BalanceMastersCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/BalanceMastersCommand.java
@@ -23,13 +23,15 @@
 import org.onlab.onos.mastership.MastershipAdminService;
 import org.onlab.onos.mastership.MastershipService;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.device.DeviceService;
 
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static org.onlab.onos.net.MastershipRole.MASTER;
 
 /**
  * Forces device mastership rebalancing.
@@ -50,73 +52,62 @@
 
         // Create buckets reflecting current ownership.
         for (ControllerNode node : nodes) {
-            controllerDevices.putAll(node, mastershipService.getDevicesOf(node.id()));
+            Set<DeviceId> devicesOf = mastershipService.getDevicesOf(node.id());
+            controllerDevices.putAll(node, devicesOf);
+            print("Node %s has %d devices.", node.id(), devicesOf.size());
         }
 
-        int bucketCount = nodes.size();
-        for (int i = 0; i < bucketCount / 2; i++) {
+        int rounds = nodes.size();
+        for (int i = 0; i < rounds; i++) {
             // Iterate over the buckets and find the smallest and the largest.
-            ControllerNode smallest = findSmallestBucket(controllerDevices);
-            ControllerNode largest = findLargestBucket(controllerDevices);
-            balanceBuckets(smallest, largest, controllerDevices,
-                           mastershipService, adminService);
+            ControllerNode smallest = findBucket(true, nodes, controllerDevices);
+            ControllerNode largest = findBucket(false, nodes, controllerDevices);
+            balanceBuckets(smallest, largest, controllerDevices, adminService);
         }
     }
 
-    private ControllerNode findSmallestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
-        int minSize = Integer.MAX_VALUE;
-        ControllerNode minNode = null;
-        for (ControllerNode node : controllerDevices.keySet()) {
+    private ControllerNode findBucket(boolean min, Collection<ControllerNode> nodes,
+                                      Multimap<ControllerNode, DeviceId> controllerDevices) {
+        int xSize = min ? Integer.MAX_VALUE : -1;
+        ControllerNode xNode = null;
+        for (ControllerNode node : nodes) {
             int size = controllerDevices.get(node).size();
-            if (size < minSize) {
-                minSize = size;
-                minNode = node;
+            if ((min && size < xSize) || (!min && size > xSize)) {
+                xSize = size;
+                xNode = node;
             }
         }
-        return minNode;
-    }
-
-    private ControllerNode findLargestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
-        int maxSize = -1;
-        ControllerNode maxNode = null;
-        for (ControllerNode node : controllerDevices.keySet()) {
-            int size = controllerDevices.get(node).size();
-            if (size >= maxSize) {
-                maxSize = size;
-                maxNode = node;
-            }
-        }
-        return maxNode;
+        return xNode;
     }
 
     // FIXME: enhance to better handle cases where smallest cannot take any of the devices from largest
 
     private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
                                 Multimap<ControllerNode, DeviceId> controllerDevices,
-                                MastershipService mastershipService,
                                 MastershipAdminService adminService) {
         Collection<DeviceId> minBucket = controllerDevices.get(smallest);
         Collection<DeviceId> maxBucket = controllerDevices.get(largest);
+        int bucketCount = controllerDevices.keySet().size();
+        int deviceCount = get(DeviceService.class).getDeviceCount();
 
         int delta = (maxBucket.size() - minBucket.size()) / 2;
+        delta = Math.min(deviceCount / bucketCount, delta);
 
-        print("Attempting to move %d nodes from %s to %s...",
-              delta, largest.id(), smallest.id());
+        if (delta > 0) {
+            print("Attempting to move %d nodes from %s to %s...",
+                  delta, largest.id(), smallest.id());
 
-        int i = 0;
-        Iterator<DeviceId> it = maxBucket.iterator();
-        while (it.hasNext() && i < delta) {
-            DeviceId deviceId = it.next();
-
-            // Check that the transfer can happen for the current element.
-            if (mastershipService.getNodesFor(deviceId).backups().contains(smallest.id())) {
-                print("Setting %s as the new master for %s", smallest.id(), deviceId);
-                adminService.setRole(smallest.id(), deviceId, MastershipRole.MASTER);
+            int i = 0;
+            Iterator<DeviceId> it = maxBucket.iterator();
+            while (it.hasNext() && i < delta) {
+                DeviceId deviceId = it.next();
+                print("Setting %s as the master for %s", smallest.id(), deviceId);
+                adminService.setRole(smallest.id(), deviceId, MASTER);
+                controllerDevices.put(smallest, deviceId);
+                it.remove();
                 i++;
             }
         }
-
-        controllerDevices.removeAll(smallest);
     }
 
 }