blob: 1f4c17dae52c15d4363101ed9b731722c951e73d [file] [log] [blame]
Thomas Vachuskae02e11c2014-11-24 16:13:52 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onlab.onos.cli;
17
18import com.google.common.collect.HashMultimap;
19import com.google.common.collect.Multimap;
20import org.apache.karaf.shell.commands.Command;
21import org.onlab.onos.cluster.ClusterService;
22import org.onlab.onos.cluster.ControllerNode;
23import org.onlab.onos.mastership.MastershipAdminService;
24import org.onlab.onos.mastership.MastershipService;
25import org.onlab.onos.net.DeviceId;
26import org.onlab.onos.net.MastershipRole;
27
28import java.util.Collection;
29import java.util.Iterator;
30import java.util.List;
31
32import static com.google.common.collect.Lists.newArrayList;
33
34/**
35 * Forces device mastership rebalancing.
36 */
37@Command(scope = "onos", name = "balance-masters",
38 description = "Forces device mastership rebalancing")
39public class BalanceMastersCommand extends AbstractShellCommand {
40
41 @Override
42 protected void execute() {
43 ClusterService service = get(ClusterService.class);
44 MastershipService mastershipService = get(MastershipService.class);
45 MastershipAdminService adminService = get(MastershipAdminService.class);
46
47 List<ControllerNode> nodes = newArrayList(service.getNodes());
48
49 Multimap<ControllerNode, DeviceId> controllerDevices = HashMultimap.create();
50
51 // Create buckets reflecting current ownership.
52 for (ControllerNode node : nodes) {
53 controllerDevices.putAll(node, mastershipService.getDevicesOf(node.id()));
54 }
55
56 int bucketCount = nodes.size();
57 for (int i = 0; i < bucketCount / 2; i++) {
58 // Iterate over the buckets and find the smallest and the largest.
59 ControllerNode smallest = findSmallestBucket(controllerDevices);
60 ControllerNode largest = findLargestBucket(controllerDevices);
61 balanceBuckets(smallest, largest, controllerDevices,
62 mastershipService, adminService);
63 }
64 }
65
66 private ControllerNode findSmallestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
67 int minSize = Integer.MAX_VALUE;
68 ControllerNode minNode = null;
69 for (ControllerNode node : controllerDevices.keySet()) {
70 int size = controllerDevices.get(node).size();
71 if (size < minSize) {
72 minSize = size;
73 minNode = node;
74 }
75 }
76 return minNode;
77 }
78
79 private ControllerNode findLargestBucket(Multimap<ControllerNode, DeviceId> controllerDevices) {
80 int maxSize = -1;
81 ControllerNode maxNode = null;
82 for (ControllerNode node : controllerDevices.keySet()) {
83 int size = controllerDevices.get(node).size();
84 if (size >= maxSize) {
85 maxSize = size;
86 maxNode = node;
87 }
88 }
89 return maxNode;
90 }
91
92 // FIXME: enhance to better handle cases where smallest cannot take any of the devices from largest
93
94 private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
95 Multimap<ControllerNode, DeviceId> controllerDevices,
96 MastershipService mastershipService,
97 MastershipAdminService adminService) {
98 Collection<DeviceId> minBucket = controllerDevices.get(smallest);
99 Collection<DeviceId> maxBucket = controllerDevices.get(largest);
100
101 int delta = (maxBucket.size() - minBucket.size()) / 2;
102
103 print("Attempting to move %d nodes from %s to %s...",
104 delta, largest.id(), smallest.id());
105
106 int i = 0;
107 Iterator<DeviceId> it = maxBucket.iterator();
108 while (it.hasNext() && i < delta) {
109 DeviceId deviceId = it.next();
110
111 // Check that the transfer can happen for the current element.
112 if (mastershipService.getNodesFor(deviceId).backups().contains(smallest)) {
113 print("Setting %s as the new master for %s", smallest.id(), deviceId);
114 adminService.setRole(smallest.id(), deviceId, MastershipRole.MASTER);
115 i++;
116 }
117 }
118
119 controllerDevices.removeAll(smallest);
120 }
121
122}