blob: e746d9262d5c7f875ac5716f50b7f1cba3fdb9c9 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.cluster.impl;
Ayaka Koshibe16609692014-09-23 12:46:15 -070017
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080018import com.codahale.metrics.Timer;
19import com.codahale.metrics.Timer.Context;
Madan Jampanide003d92015-05-11 17:14:20 -070020import com.google.common.collect.Lists;
21import com.google.common.util.concurrent.Futures;
Ayaka Koshibe16609692014-09-23 12:46:15 -070022import org.apache.felix.scr.annotations.Activate;
tom4a5d1712014-09-23 17:49:39 -070023import org.apache.felix.scr.annotations.Component;
Ayaka Koshibe16609692014-09-23 12:46:15 -070024import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
tom4a5d1712014-09-23 17:49:39 -070027import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080028import org.onlab.metrics.MetricsService;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.ControllerNode;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.cluster.RoleInfo;
33import org.onosproject.core.MetricsHelper;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080034import org.onosproject.event.AbstractListenerManager;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.mastership.MastershipAdminService;
36import org.onosproject.mastership.MastershipEvent;
37import org.onosproject.mastership.MastershipListener;
38import org.onosproject.mastership.MastershipService;
39import org.onosproject.mastership.MastershipStore;
40import org.onosproject.mastership.MastershipStoreDelegate;
41import org.onosproject.mastership.MastershipTerm;
42import org.onosproject.mastership.MastershipTermService;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.MastershipRole;
Ayaka Koshibe16609692014-09-23 12:46:15 -070045import org.slf4j.Logger;
46
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080047import java.util.Collection;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -080048import java.util.HashMap;
49import java.util.HashSet;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080050import java.util.Iterator;
51import java.util.List;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -080052import java.util.Map;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080053import java.util.Set;
Madan Jampanif7536ab2015-05-07 23:23:23 -070054import java.util.concurrent.CompletableFuture;
55
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080056import static com.google.common.base.Preconditions.checkNotNull;
57import static com.google.common.collect.Lists.newArrayList;
58import static org.onlab.metrics.MetricsUtil.startTimer;
59import static org.onlab.metrics.MetricsUtil.stopTimer;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import static org.onosproject.net.MastershipRole.MASTER;
Changhoon Yoon541ef712015-05-23 17:18:34 +090061import static org.onosproject.security.AppGuard.checkPermission;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080062import static org.onosproject.security.AppPermission.Type.CLUSTER_READ;
63import static org.onosproject.security.AppPermission.Type.CLUSTER_WRITE;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070064import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoonb856b812015-08-10 03:47:19 +090065
Changhoon Yoon541ef712015-05-23 17:18:34 +090066
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080067
tom4a5d1712014-09-23 17:49:39 -070068@Component(immediate = true)
69@Service
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -070070public class MastershipManager
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070071 extends AbstractListenerManager<MastershipEvent, MastershipListener>
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -080072 implements MastershipService, MastershipAdminService, MastershipTermService,
73 MetricsHelper {
Ayaka Koshibe16609692014-09-23 12:46:15 -070074
75 private static final String NODE_ID_NULL = "Node ID cannot be null";
76 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
77 private static final String ROLE_NULL = "Mastership role cannot be null";
78
79 private final Logger log = getLogger(getClass());
80
alshabib339a3d92014-09-26 17:54:32 -070081 private final MastershipStoreDelegate delegate = new InternalDelegate();
Ayaka Koshibe16609692014-09-23 12:46:15 -070082
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected MastershipStore store;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom4a5d1712014-09-23 17:49:39 -070087 protected ClusterService clusterService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070088
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected MetricsService metricsService;
91
Madan Jampanic6e574f2015-05-29 13:41:52 -070092 private NodeId localNodeId;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080093 private Timer requestRoleTimer;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070094
Ayaka Koshibe16609692014-09-23 12:46:15 -070095 @Activate
96 public void activate() {
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080097 requestRoleTimer = createTimer("Mastership", "requestRole", "responseTime");
Madan Jampanic6e574f2015-05-29 13:41:52 -070098 localNodeId = clusterService.getLocalNode().id();
Ayaka Koshibe16609692014-09-23 12:46:15 -070099 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
alshabib339a3d92014-09-26 17:54:32 -0700100 store.setDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700101 log.info("Started");
102 }
103
104 @Deactivate
105 public void deactivate() {
106 eventDispatcher.removeSink(MastershipEvent.class);
alshabib339a3d92014-09-26 17:54:32 -0700107 store.unsetDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700108 log.info("Stopped");
109 }
110
Ayaka Koshibe16609692014-09-23 12:46:15 -0700111 @Override
Madan Jampanide003d92015-05-11 17:14:20 -0700112 public CompletableFuture<Void> setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
Ayaka Koshibe16609692014-09-23 12:46:15 -0700113 checkNotNull(nodeId, NODE_ID_NULL);
114 checkNotNull(deviceId, DEVICE_ID_NULL);
115 checkNotNull(role, ROLE_NULL);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700116
Madan Jampanif7536ab2015-05-07 23:23:23 -0700117 CompletableFuture<MastershipEvent> eventFuture = null;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700118
119 switch (role) {
120 case MASTER:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700121 eventFuture = store.setMaster(nodeId, deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700122 break;
123 case STANDBY:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700124 eventFuture = store.setStandby(nodeId, deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700125 break;
126 case NONE:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700127 eventFuture = store.relinquishRole(nodeId, deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700128 break;
129 default:
130 log.info("Unknown role; ignoring");
Madan Jampanide003d92015-05-11 17:14:20 -0700131 return CompletableFuture.completedFuture(null);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700132 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700133
Madan Jampanic6e574f2015-05-29 13:41:52 -0700134 return eventFuture.thenAccept(this::post)
135 .thenApply(v -> null);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700136 }
137
138 @Override
tomb41d1ac2014-09-24 01:51:24 -0700139 public MastershipRole getLocalRole(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900140 checkPermission(CLUSTER_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900141
tomb41d1ac2014-09-24 01:51:24 -0700142 checkNotNull(deviceId, DEVICE_ID_NULL);
143 return store.getRole(clusterService.getLocalNode().id(), deviceId);
144 }
145
146 @Override
Madan Jampanic6e574f2015-05-29 13:41:52 -0700147 public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900148 checkPermission(CLUSTER_WRITE);
Madan Jampanic6e574f2015-05-29 13:41:52 -0700149 return store.relinquishRole(localNodeId, deviceId)
150 .thenAccept(this::post)
151 .thenApply(v -> null);
tomb41d1ac2014-09-24 01:51:24 -0700152 }
153
154 @Override
Madan Jampanide003d92015-05-11 17:14:20 -0700155 public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900156 checkPermission(CLUSTER_WRITE);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900157
tomb41d1ac2014-09-24 01:51:24 -0700158 checkNotNull(deviceId, DEVICE_ID_NULL);
Yuta HIGUCHI6a462832014-11-23 23:56:03 -0800159 final Context timer = startTimer(requestRoleTimer);
Madan Jampanide003d92015-05-11 17:14:20 -0700160 return store.requestRole(deviceId).whenComplete((result, error) -> stopTimer(timer));
161
tomb41d1ac2014-09-24 01:51:24 -0700162 }
163
164 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700165 public NodeId getMasterFor(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900166 checkPermission(CLUSTER_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900167
Ayaka Koshibe16609692014-09-23 12:46:15 -0700168 checkNotNull(deviceId, DEVICE_ID_NULL);
169 return store.getMaster(deviceId);
170 }
171
172 @Override
173 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900174 checkPermission(CLUSTER_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900175
Ayaka Koshibe16609692014-09-23 12:46:15 -0700176 checkNotNull(nodeId, NODE_ID_NULL);
177 return store.getDevices(nodeId);
178 }
179
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700180 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700181 public RoleInfo getNodesFor(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900182 checkPermission(CLUSTER_READ);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900183
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700184 checkNotNull(deviceId, DEVICE_ID_NULL);
185 return store.getNodes(deviceId);
186 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700187
188 @Override
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800189 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900190 checkPermission(CLUSTER_READ);
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800191 return store.getTermFor(deviceId);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700192 }
193
Ayaka Koshibe16609692014-09-23 12:46:15 -0700194 @Override
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -0800195 public MetricsService metricsService() {
196 return metricsService;
197 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700198
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800199 @Override
200 public void balanceRoles() {
201 List<ControllerNode> nodes = newArrayList(clusterService.getNodes());
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800202 Map<ControllerNode, Set<DeviceId>> controllerDevices = new HashMap<>();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800203 int deviceCount = 0;
204
205 // Create buckets reflecting current ownership.
206 for (ControllerNode node : nodes) {
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800207 if (clusterService.getState(node.id()).isActive()) {
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800208 Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id()));
209 deviceCount += devicesOf.size();
210 controllerDevices.put(node, devicesOf);
211 log.info("Node {} has {} devices.", node.id(), devicesOf.size());
212 }
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800213 }
214
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800215 // Now re-balance the buckets until they are roughly even.
Madan Jampanide003d92015-05-11 17:14:20 -0700216 List<CompletableFuture<Void>> balanceBucketsFutures = Lists.newLinkedList();
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800217 int rounds = controllerDevices.keySet().size();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800218 for (int i = 0; i < rounds; i++) {
219 // Iterate over the buckets and find the smallest and the largest.
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800220 ControllerNode smallest = findBucket(true, controllerDevices);
221 ControllerNode largest = findBucket(false, controllerDevices);
Madan Jampanide003d92015-05-11 17:14:20 -0700222 balanceBucketsFutures.add(balanceBuckets(smallest, largest, controllerDevices, deviceCount));
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800223 }
Madan Jampanide003d92015-05-11 17:14:20 -0700224 CompletableFuture<Void> balanceRolesFuture = CompletableFuture.allOf(
225 balanceBucketsFutures.toArray(new CompletableFuture[balanceBucketsFutures.size()]));
226
227 Futures.getUnchecked(balanceRolesFuture);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800228 }
229
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800230 private ControllerNode findBucket(boolean min,
231 Map<ControllerNode, Set<DeviceId>> controllerDevices) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800232 int xSize = min ? Integer.MAX_VALUE : -1;
233 ControllerNode xNode = null;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800234 for (ControllerNode node : controllerDevices.keySet()) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800235 int size = controllerDevices.get(node).size();
236 if ((min && size < xSize) || (!min && size > xSize)) {
237 xSize = size;
238 xNode = node;
239 }
240 }
241 return xNode;
242 }
243
Madan Jampanide003d92015-05-11 17:14:20 -0700244 private CompletableFuture<Void> balanceBuckets(ControllerNode smallest, ControllerNode largest,
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800245 Map<ControllerNode, Set<DeviceId>> controllerDevices,
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800246 int deviceCount) {
247 Collection<DeviceId> minBucket = controllerDevices.get(smallest);
248 Collection<DeviceId> maxBucket = controllerDevices.get(largest);
249 int bucketCount = controllerDevices.keySet().size();
250
251 int delta = (maxBucket.size() - minBucket.size()) / 2;
252 delta = Math.min(deviceCount / bucketCount, delta);
253
Madan Jampanide003d92015-05-11 17:14:20 -0700254 List<CompletableFuture<Void>> setRoleFutures = Lists.newLinkedList();
255
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800256 if (delta > 0) {
257 log.info("Attempting to move {} nodes from {} to {}...", delta,
258 largest.id(), smallest.id());
259
260 int i = 0;
261 Iterator<DeviceId> it = maxBucket.iterator();
262 while (it.hasNext() && i < delta) {
263 DeviceId deviceId = it.next();
264 log.info("Setting {} as the master for {}", smallest.id(), deviceId);
Madan Jampanide003d92015-05-11 17:14:20 -0700265 setRoleFutures.add(setRole(smallest.id(), deviceId, MASTER));
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800266 controllerDevices.get(smallest).add(deviceId);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800267 it.remove();
268 i++;
269 }
270 }
Madan Jampanide003d92015-05-11 17:14:20 -0700271
272 return CompletableFuture.allOf(setRoleFutures.toArray(new CompletableFuture[setRoleFutures.size()]));
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800273 }
274
275
alshabib339a3d92014-09-26 17:54:32 -0700276 public class InternalDelegate implements MastershipStoreDelegate {
alshabib339a3d92014-09-26 17:54:32 -0700277 @Override
278 public void notify(MastershipEvent event) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700279 post(event);
alshabib339a3d92014-09-26 17:54:32 -0700280 }
alshabib339a3d92014-09-26 17:54:32 -0700281 }
282
Ayaka Koshibe16609692014-09-23 12:46:15 -0700283}