blob: 77d0c16ebe14790a2c274b5118dfed5dd33e1363 [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;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080026import org.onlab.onos.net.device.DeviceService;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080027
28import java.util.Collection;
29import java.util.Iterator;
30import java.util.List;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080031import java.util.Set;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080032
33import static com.google.common.collect.Lists.newArrayList;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080034import static org.onlab.onos.net.MastershipRole.MASTER;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080035
36/**
37 * Forces device mastership rebalancing.
38 */
39@Command(scope = "onos", name = "balance-masters",
40 description = "Forces device mastership rebalancing")
41public class BalanceMastersCommand extends AbstractShellCommand {
42
43 @Override
44 protected void execute() {
45 ClusterService service = get(ClusterService.class);
46 MastershipService mastershipService = get(MastershipService.class);
47 MastershipAdminService adminService = get(MastershipAdminService.class);
48
49 List<ControllerNode> nodes = newArrayList(service.getNodes());
50
51 Multimap<ControllerNode, DeviceId> controllerDevices = HashMultimap.create();
52
53 // Create buckets reflecting current ownership.
54 for (ControllerNode node : nodes) {
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080055 Set<DeviceId> devicesOf = mastershipService.getDevicesOf(node.id());
56 controllerDevices.putAll(node, devicesOf);
57 print("Node %s has %d devices.", node.id(), devicesOf.size());
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080058 }
59
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080060 int rounds = nodes.size();
61 for (int i = 0; i < rounds; i++) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080062 // Iterate over the buckets and find the smallest and the largest.
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080063 ControllerNode smallest = findBucket(true, nodes, controllerDevices);
64 ControllerNode largest = findBucket(false, nodes, controllerDevices);
65 balanceBuckets(smallest, largest, controllerDevices, adminService);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080066 }
67 }
68
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080069 private ControllerNode findBucket(boolean min, Collection<ControllerNode> nodes,
70 Multimap<ControllerNode, DeviceId> controllerDevices) {
71 int xSize = min ? Integer.MAX_VALUE : -1;
72 ControllerNode xNode = null;
73 for (ControllerNode node : nodes) {
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080074 int size = controllerDevices.get(node).size();
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080075 if ((min && size < xSize) || (!min && size > xSize)) {
76 xSize = size;
77 xNode = node;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080078 }
79 }
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080080 return xNode;
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080081 }
82
83 // FIXME: enhance to better handle cases where smallest cannot take any of the devices from largest
84
85 private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
86 Multimap<ControllerNode, DeviceId> controllerDevices,
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080087 MastershipAdminService adminService) {
88 Collection<DeviceId> minBucket = controllerDevices.get(smallest);
89 Collection<DeviceId> maxBucket = controllerDevices.get(largest);
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080090 int bucketCount = controllerDevices.keySet().size();
91 int deviceCount = get(DeviceService.class).getDeviceCount();
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080092
93 int delta = (maxBucket.size() - minBucket.size()) / 2;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080094 delta = Math.min(deviceCount / bucketCount, delta);
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080095
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080096 if (delta > 0) {
97 print("Attempting to move %d nodes from %s to %s...",
98 delta, largest.id(), smallest.id());
Thomas Vachuskae02e11c2014-11-24 16:13:52 -080099
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800100 int i = 0;
101 Iterator<DeviceId> it = maxBucket.iterator();
102 while (it.hasNext() && i < delta) {
103 DeviceId deviceId = it.next();
104 print("Setting %s as the master for %s", smallest.id(), deviceId);
105 adminService.setRole(smallest.id(), deviceId, MASTER);
106 controllerDevices.put(smallest, deviceId);
107 it.remove();
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800108 i++;
109 }
110 }
Thomas Vachuskae02e11c2014-11-24 16:13:52 -0800111 }
112
113}