blob: 1ac073a0f2a6465cbdd70a0226ced44efbd7853a [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;
Madan Jampanif7536ab2015-05-07 23:23:23 -070022
Ayaka Koshibe16609692014-09-23 12:46:15 -070023import org.apache.felix.scr.annotations.Activate;
tom4a5d1712014-09-23 17:49:39 -070024import org.apache.felix.scr.annotations.Component;
Ayaka Koshibe16609692014-09-23 12:46:15 -070025import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
tom4a5d1712014-09-23 17:49:39 -070028import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080029import org.onlab.metrics.MetricsService;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.cluster.ClusterService;
31import org.onosproject.cluster.ControllerNode;
32import org.onosproject.cluster.NodeId;
33import org.onosproject.cluster.RoleInfo;
34import org.onosproject.core.MetricsHelper;
Changhoon Yoon541ef712015-05-23 17:18:34 +090035import org.onosproject.core.Permission;
Simon Huntff663742015-05-14 13:33:05 -070036import org.onosproject.event.ListenerRegistry;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.event.EventDeliveryService;
38import org.onosproject.mastership.MastershipAdminService;
39import org.onosproject.mastership.MastershipEvent;
40import org.onosproject.mastership.MastershipListener;
41import org.onosproject.mastership.MastershipService;
42import org.onosproject.mastership.MastershipStore;
43import org.onosproject.mastership.MastershipStoreDelegate;
44import org.onosproject.mastership.MastershipTerm;
45import org.onosproject.mastership.MastershipTermService;
46import org.onosproject.net.DeviceId;
47import org.onosproject.net.MastershipRole;
Ayaka Koshibe16609692014-09-23 12:46:15 -070048import org.slf4j.Logger;
49
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080050import java.util.Collection;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -080051import java.util.HashMap;
52import java.util.HashSet;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080053import java.util.Iterator;
54import java.util.List;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -080055import java.util.Map;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080056import java.util.Set;
Madan Jampanif7536ab2015-05-07 23:23:23 -070057import java.util.concurrent.CompletableFuture;
58
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080059import static com.google.common.base.Preconditions.checkNotNull;
60import static com.google.common.collect.Lists.newArrayList;
61import static org.onlab.metrics.MetricsUtil.startTimer;
62import static org.onlab.metrics.MetricsUtil.stopTimer;
Brian O'Connorabafb502014-12-02 22:26:20 -080063import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
64import static org.onosproject.net.MastershipRole.MASTER;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080065import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoon541ef712015-05-23 17:18:34 +090066import static org.onosproject.security.AppGuard.checkPermission;
67
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080068
tom4a5d1712014-09-23 17:49:39 -070069@Component(immediate = true)
70@Service
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -070071public class MastershipManager
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
Simon Huntff663742015-05-14 13:33:05 -070081 protected final ListenerRegistry<MastershipEvent, MastershipListener>
82 listenerRegistry = new ListenerRegistry<>();
alshabib339a3d92014-09-26 17:54:32 -070083
84 private final MastershipStoreDelegate delegate = new InternalDelegate();
Ayaka Koshibe16609692014-09-23 12:46:15 -070085
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected MastershipStore store;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected EventDeliveryService eventDispatcher;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom4a5d1712014-09-23 17:49:39 -070093 protected ClusterService clusterService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070094
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected MetricsService metricsService;
97
Madan Jampanic6e574f2015-05-29 13:41:52 -070098 private NodeId localNodeId;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080099 private Timer requestRoleTimer;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700100
Ayaka Koshibe16609692014-09-23 12:46:15 -0700101 @Activate
102 public void activate() {
Yuta HIGUCHI6a462832014-11-23 23:56:03 -0800103 requestRoleTimer = createTimer("Mastership", "requestRole", "responseTime");
Madan Jampanic6e574f2015-05-29 13:41:52 -0700104 localNodeId = clusterService.getLocalNode().id();
Ayaka Koshibe16609692014-09-23 12:46:15 -0700105 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
alshabib339a3d92014-09-26 17:54:32 -0700106 store.setDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700107 log.info("Started");
108 }
109
110 @Deactivate
111 public void deactivate() {
112 eventDispatcher.removeSink(MastershipEvent.class);
alshabib339a3d92014-09-26 17:54:32 -0700113 store.unsetDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700114 log.info("Stopped");
115 }
116
Ayaka Koshibe16609692014-09-23 12:46:15 -0700117 @Override
Madan Jampanide003d92015-05-11 17:14:20 -0700118 public CompletableFuture<Void> setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
Ayaka Koshibe16609692014-09-23 12:46:15 -0700119 checkNotNull(nodeId, NODE_ID_NULL);
120 checkNotNull(deviceId, DEVICE_ID_NULL);
121 checkNotNull(role, ROLE_NULL);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700122
Madan Jampanif7536ab2015-05-07 23:23:23 -0700123 CompletableFuture<MastershipEvent> eventFuture = null;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700124
125 switch (role) {
126 case MASTER:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700127 eventFuture = store.setMaster(nodeId, deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700128 break;
129 case STANDBY:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700130 eventFuture = store.setStandby(nodeId, deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700131 break;
132 case NONE:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700133 eventFuture = store.relinquishRole(nodeId, deviceId);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700134 break;
135 default:
136 log.info("Unknown role; ignoring");
Madan Jampanide003d92015-05-11 17:14:20 -0700137 return CompletableFuture.completedFuture(null);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700138 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700139
Madan Jampanic6e574f2015-05-29 13:41:52 -0700140 return eventFuture.thenAccept(this::post)
141 .thenApply(v -> null);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700142 }
143
144 @Override
tomb41d1ac2014-09-24 01:51:24 -0700145 public MastershipRole getLocalRole(DeviceId deviceId) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900146 checkPermission(Permission.CLUSTER_READ);
147
tomb41d1ac2014-09-24 01:51:24 -0700148 checkNotNull(deviceId, DEVICE_ID_NULL);
149 return store.getRole(clusterService.getLocalNode().id(), deviceId);
150 }
151
152 @Override
Madan Jampanic6e574f2015-05-29 13:41:52 -0700153 public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900154 checkPermission(Permission.CLUSTER_WRITE);
Madan Jampanic6e574f2015-05-29 13:41:52 -0700155 return store.relinquishRole(localNodeId, deviceId)
156 .thenAccept(this::post)
157 .thenApply(v -> null);
tomb41d1ac2014-09-24 01:51:24 -0700158 }
159
160 @Override
Madan Jampanide003d92015-05-11 17:14:20 -0700161 public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900162 checkPermission(Permission.CLUSTER_WRITE);
163
tomb41d1ac2014-09-24 01:51:24 -0700164 checkNotNull(deviceId, DEVICE_ID_NULL);
Yuta HIGUCHI6a462832014-11-23 23:56:03 -0800165 final Context timer = startTimer(requestRoleTimer);
Madan Jampanide003d92015-05-11 17:14:20 -0700166 return store.requestRole(deviceId).whenComplete((result, error) -> stopTimer(timer));
167
tomb41d1ac2014-09-24 01:51:24 -0700168 }
169
170 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700171 public NodeId getMasterFor(DeviceId deviceId) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900172 checkPermission(Permission.CLUSTER_READ);
173
Ayaka Koshibe16609692014-09-23 12:46:15 -0700174 checkNotNull(deviceId, DEVICE_ID_NULL);
175 return store.getMaster(deviceId);
176 }
177
178 @Override
179 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900180 checkPermission(Permission.CLUSTER_READ);
181
Ayaka Koshibe16609692014-09-23 12:46:15 -0700182 checkNotNull(nodeId, NODE_ID_NULL);
183 return store.getDevices(nodeId);
184 }
185
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700186 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700187 public RoleInfo getNodesFor(DeviceId deviceId) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900188 checkPermission(Permission.CLUSTER_READ);
189
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700190 checkNotNull(deviceId, DEVICE_ID_NULL);
191 return store.getNodes(deviceId);
192 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700193
194 @Override
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800195 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
196 return store.getTermFor(deviceId);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700197 }
198
Ayaka Koshibe16609692014-09-23 12:46:15 -0700199 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700200 public void addListener(MastershipListener listener) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900201 checkPermission(Permission.CLUSTER_EVENT);
202
Ayaka Koshibe16609692014-09-23 12:46:15 -0700203 checkNotNull(listener);
204 listenerRegistry.addListener(listener);
205 }
206
207 @Override
208 public void removeListener(MastershipListener listener) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900209 checkPermission(Permission.CLUSTER_EVENT);
210
Ayaka Koshibe16609692014-09-23 12:46:15 -0700211 checkNotNull(listener);
212 listenerRegistry.removeListener(listener);
213 }
214
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -0800215 @Override
216 public MetricsService metricsService() {
217 return metricsService;
218 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700219
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800220 @Override
221 public void balanceRoles() {
222 List<ControllerNode> nodes = newArrayList(clusterService.getNodes());
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800223 Map<ControllerNode, Set<DeviceId>> controllerDevices = new HashMap<>();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800224 int deviceCount = 0;
225
226 // Create buckets reflecting current ownership.
227 for (ControllerNode node : nodes) {
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800228 if (clusterService.getState(node.id()) == ACTIVE) {
229 Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id()));
230 deviceCount += devicesOf.size();
231 controllerDevices.put(node, devicesOf);
232 log.info("Node {} has {} devices.", node.id(), devicesOf.size());
233 }
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800234 }
235
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800236 // Now re-balance the buckets until they are roughly even.
Madan Jampanide003d92015-05-11 17:14:20 -0700237 List<CompletableFuture<Void>> balanceBucketsFutures = Lists.newLinkedList();
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800238 int rounds = controllerDevices.keySet().size();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800239 for (int i = 0; i < rounds; i++) {
240 // Iterate over the buckets and find the smallest and the largest.
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800241 ControllerNode smallest = findBucket(true, controllerDevices);
242 ControllerNode largest = findBucket(false, controllerDevices);
Madan Jampanide003d92015-05-11 17:14:20 -0700243 balanceBucketsFutures.add(balanceBuckets(smallest, largest, controllerDevices, deviceCount));
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800244 }
Madan Jampanide003d92015-05-11 17:14:20 -0700245 CompletableFuture<Void> balanceRolesFuture = CompletableFuture.allOf(
246 balanceBucketsFutures.toArray(new CompletableFuture[balanceBucketsFutures.size()]));
247
248 Futures.getUnchecked(balanceRolesFuture);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800249 }
250
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800251 private ControllerNode findBucket(boolean min,
252 Map<ControllerNode, Set<DeviceId>> controllerDevices) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800253 int xSize = min ? Integer.MAX_VALUE : -1;
254 ControllerNode xNode = null;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800255 for (ControllerNode node : controllerDevices.keySet()) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800256 int size = controllerDevices.get(node).size();
257 if ((min && size < xSize) || (!min && size > xSize)) {
258 xSize = size;
259 xNode = node;
260 }
261 }
262 return xNode;
263 }
264
Madan Jampanide003d92015-05-11 17:14:20 -0700265 private CompletableFuture<Void> balanceBuckets(ControllerNode smallest, ControllerNode largest,
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800266 Map<ControllerNode, Set<DeviceId>> controllerDevices,
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800267 int deviceCount) {
268 Collection<DeviceId> minBucket = controllerDevices.get(smallest);
269 Collection<DeviceId> maxBucket = controllerDevices.get(largest);
270 int bucketCount = controllerDevices.keySet().size();
271
272 int delta = (maxBucket.size() - minBucket.size()) / 2;
273 delta = Math.min(deviceCount / bucketCount, delta);
274
Madan Jampanide003d92015-05-11 17:14:20 -0700275 List<CompletableFuture<Void>> setRoleFutures = Lists.newLinkedList();
276
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800277 if (delta > 0) {
278 log.info("Attempting to move {} nodes from {} to {}...", delta,
279 largest.id(), smallest.id());
280
281 int i = 0;
282 Iterator<DeviceId> it = maxBucket.iterator();
283 while (it.hasNext() && i < delta) {
284 DeviceId deviceId = it.next();
285 log.info("Setting {} as the master for {}", smallest.id(), deviceId);
Madan Jampanide003d92015-05-11 17:14:20 -0700286 setRoleFutures.add(setRole(smallest.id(), deviceId, MASTER));
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800287 controllerDevices.get(smallest).add(deviceId);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800288 it.remove();
289 i++;
290 }
291 }
Madan Jampanide003d92015-05-11 17:14:20 -0700292
293 return CompletableFuture.allOf(setRoleFutures.toArray(new CompletableFuture[setRoleFutures.size()]));
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800294 }
295
296
Ayaka Koshibe16609692014-09-23 12:46:15 -0700297 // Posts the specified event to the local event dispatcher.
298 private void post(MastershipEvent event) {
299 if (event != null && eventDispatcher != null) {
300 eventDispatcher.post(event);
301 }
302 }
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -0700303
alshabib339a3d92014-09-26 17:54:32 -0700304 public class InternalDelegate implements MastershipStoreDelegate {
305
306 @Override
307 public void notify(MastershipEvent event) {
Yuta HIGUCHI9e11ac02014-11-12 10:09:49 -0800308 log.trace("dispatching mastership event {}", event);
alshabib339a3d92014-09-26 17:54:32 -0700309 eventDispatcher.post(event);
310 }
311
312 }
313
Ayaka Koshibe16609692014-09-23 12:46:15 -0700314}