blob: cb2fd1880cd9fa4a9c362dccefd35cca420964b0 [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 Jampanif7536ab2015-05-07 23:23:23 -070020
Ayaka Koshibe16609692014-09-23 12:46:15 -070021import org.apache.felix.scr.annotations.Activate;
tom4a5d1712014-09-23 17:49:39 -070022import org.apache.felix.scr.annotations.Component;
Ayaka Koshibe16609692014-09-23 12:46:15 -070023import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
tom4a5d1712014-09-23 17:49:39 -070026import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080027import org.onlab.metrics.MetricsService;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.ControllerNode;
30import org.onosproject.cluster.NodeId;
31import org.onosproject.cluster.RoleInfo;
32import org.onosproject.core.MetricsHelper;
33import org.onosproject.event.AbstractListenerRegistry;
34import org.onosproject.event.EventDeliveryService;
35import 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.cluster.ControllerNode.State.ACTIVE;
61import static org.onosproject.net.MastershipRole.MASTER;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080062import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080063
tom4a5d1712014-09-23 17:49:39 -070064@Component(immediate = true)
65@Service
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -070066public class MastershipManager
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -080067 implements MastershipService, MastershipAdminService, MastershipTermService,
68 MetricsHelper {
Ayaka Koshibe16609692014-09-23 12:46:15 -070069
70 private static final String NODE_ID_NULL = "Node ID cannot be null";
71 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
72 private static final String ROLE_NULL = "Mastership role cannot be null";
73
74 private final Logger log = getLogger(getClass());
75
76 protected final AbstractListenerRegistry<MastershipEvent, MastershipListener>
alshabib339a3d92014-09-26 17:54:32 -070077 listenerRegistry = new AbstractListenerRegistry<>();
78
79 private final MastershipStoreDelegate delegate = new InternalDelegate();
Ayaka Koshibe16609692014-09-23 12:46:15 -070080
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected MastershipStore store;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected EventDeliveryService eventDispatcher;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom4a5d1712014-09-23 17:49:39 -070088 protected ClusterService clusterService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070089
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080090 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected MetricsService metricsService;
92
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");
98
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
112 public void setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
113 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");
131 return;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700132 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700133
Madan Jampanif7536ab2015-05-07 23:23:23 -0700134 eventFuture.whenComplete((event, error) -> {
135 if (event != null) {
136 post(event);
137 }
138 });
Ayaka Koshibe16609692014-09-23 12:46:15 -0700139 }
140
141 @Override
tomb41d1ac2014-09-24 01:51:24 -0700142 public MastershipRole getLocalRole(DeviceId deviceId) {
143 checkNotNull(deviceId, DEVICE_ID_NULL);
144 return store.getRole(clusterService.getLocalNode().id(), deviceId);
145 }
146
147 @Override
148 public void relinquishMastership(DeviceId deviceId) {
Madan Jampanif7536ab2015-05-07 23:23:23 -0700149 store.relinquishRole(clusterService.getLocalNode().id(), deviceId)
150 .whenComplete((event, error) -> {
151 if (event != null) {
152 post(event);
153 }
154 });
tomb41d1ac2014-09-24 01:51:24 -0700155 }
156
157 @Override
158 public MastershipRole requestRoleFor(DeviceId deviceId) {
159 checkNotNull(deviceId, DEVICE_ID_NULL);
Yuta HIGUCHI6a462832014-11-23 23:56:03 -0800160 final Context timer = startTimer(requestRoleTimer);
161 try {
162 return store.requestRole(deviceId);
163 } finally {
164 stopTimer(timer);
165 }
tomb41d1ac2014-09-24 01:51:24 -0700166 }
167
168 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700169 public NodeId getMasterFor(DeviceId deviceId) {
170 checkNotNull(deviceId, DEVICE_ID_NULL);
171 return store.getMaster(deviceId);
172 }
173
174 @Override
175 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
176 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) {
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700182 checkNotNull(deviceId, DEVICE_ID_NULL);
183 return store.getNodes(deviceId);
184 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700185
186 @Override
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800187 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
188 return store.getTermFor(deviceId);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700189 }
190
Ayaka Koshibe16609692014-09-23 12:46:15 -0700191 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700192 public void addListener(MastershipListener listener) {
193 checkNotNull(listener);
194 listenerRegistry.addListener(listener);
195 }
196
197 @Override
198 public void removeListener(MastershipListener listener) {
199 checkNotNull(listener);
200 listenerRegistry.removeListener(listener);
201 }
202
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -0800203 @Override
204 public MetricsService metricsService() {
205 return metricsService;
206 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700207
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800208 @Override
209 public void balanceRoles() {
210 List<ControllerNode> nodes = newArrayList(clusterService.getNodes());
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800211 Map<ControllerNode, Set<DeviceId>> controllerDevices = new HashMap<>();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800212 int deviceCount = 0;
213
214 // Create buckets reflecting current ownership.
215 for (ControllerNode node : nodes) {
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800216 if (clusterService.getState(node.id()) == ACTIVE) {
217 Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id()));
218 deviceCount += devicesOf.size();
219 controllerDevices.put(node, devicesOf);
220 log.info("Node {} has {} devices.", node.id(), devicesOf.size());
221 }
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800222 }
223
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800224 // Now re-balance the buckets until they are roughly even.
225 int rounds = controllerDevices.keySet().size();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800226 for (int i = 0; i < rounds; i++) {
227 // Iterate over the buckets and find the smallest and the largest.
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800228 ControllerNode smallest = findBucket(true, controllerDevices);
229 ControllerNode largest = findBucket(false, controllerDevices);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800230 balanceBuckets(smallest, largest, controllerDevices, deviceCount);
231 }
232 }
233
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800234 private ControllerNode findBucket(boolean min,
235 Map<ControllerNode, Set<DeviceId>> controllerDevices) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800236 int xSize = min ? Integer.MAX_VALUE : -1;
237 ControllerNode xNode = null;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800238 for (ControllerNode node : controllerDevices.keySet()) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800239 int size = controllerDevices.get(node).size();
240 if ((min && size < xSize) || (!min && size > xSize)) {
241 xSize = size;
242 xNode = node;
243 }
244 }
245 return xNode;
246 }
247
248 private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800249 Map<ControllerNode, Set<DeviceId>> controllerDevices,
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800250 int deviceCount) {
251 Collection<DeviceId> minBucket = controllerDevices.get(smallest);
252 Collection<DeviceId> maxBucket = controllerDevices.get(largest);
253 int bucketCount = controllerDevices.keySet().size();
254
255 int delta = (maxBucket.size() - minBucket.size()) / 2;
256 delta = Math.min(deviceCount / bucketCount, delta);
257
258 if (delta > 0) {
259 log.info("Attempting to move {} nodes from {} to {}...", delta,
260 largest.id(), smallest.id());
261
262 int i = 0;
263 Iterator<DeviceId> it = maxBucket.iterator();
264 while (it.hasNext() && i < delta) {
265 DeviceId deviceId = it.next();
266 log.info("Setting {} as the master for {}", smallest.id(), deviceId);
267 setRole(smallest.id(), deviceId, MASTER);
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800268 controllerDevices.get(smallest).add(deviceId);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800269 it.remove();
270 i++;
271 }
272 }
273 }
274
275
Ayaka Koshibe16609692014-09-23 12:46:15 -0700276 // Posts the specified event to the local event dispatcher.
277 private void post(MastershipEvent event) {
278 if (event != null && eventDispatcher != null) {
279 eventDispatcher.post(event);
280 }
281 }
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -0700282
alshabib339a3d92014-09-26 17:54:32 -0700283 public class InternalDelegate implements MastershipStoreDelegate {
284
285 @Override
286 public void notify(MastershipEvent event) {
Yuta HIGUCHI9e11ac02014-11-12 10:09:49 -0800287 log.trace("dispatching mastership event {}", event);
alshabib339a3d92014-09-26 17:54:32 -0700288 eventDispatcher.post(event);
289 }
290
291 }
292
Ayaka Koshibe16609692014-09-23 12:46:15 -0700293}