blob: e34790def35613f9277a201787ac5cca9df79609 [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;
Ayaka Koshibe16609692014-09-23 12:46:15 -070020import org.apache.felix.scr.annotations.Activate;
tom4a5d1712014-09-23 17:49:39 -070021import org.apache.felix.scr.annotations.Component;
Ayaka Koshibe16609692014-09-23 12:46:15 -070022import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
tom4a5d1712014-09-23 17:49:39 -070025import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080026import org.onlab.metrics.MetricsService;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.cluster.ClusterService;
28import org.onosproject.cluster.ControllerNode;
29import org.onosproject.cluster.NodeId;
30import org.onosproject.cluster.RoleInfo;
31import org.onosproject.core.MetricsHelper;
32import org.onosproject.event.AbstractListenerRegistry;
33import org.onosproject.event.EventDeliveryService;
34import org.onosproject.mastership.MastershipAdminService;
35import org.onosproject.mastership.MastershipEvent;
36import org.onosproject.mastership.MastershipListener;
37import org.onosproject.mastership.MastershipService;
38import org.onosproject.mastership.MastershipStore;
39import org.onosproject.mastership.MastershipStoreDelegate;
40import org.onosproject.mastership.MastershipTerm;
41import org.onosproject.mastership.MastershipTermService;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.MastershipRole;
Ayaka Koshibe16609692014-09-23 12:46:15 -070044import org.slf4j.Logger;
45
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080046import java.util.Collection;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -080047import java.util.HashMap;
48import java.util.HashSet;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080049import java.util.Iterator;
50import java.util.List;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -080051import java.util.Map;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080052import java.util.Set;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080053import static com.google.common.base.Preconditions.checkNotNull;
54import static com.google.common.collect.Lists.newArrayList;
55import static org.onlab.metrics.MetricsUtil.startTimer;
56import static org.onlab.metrics.MetricsUtil.stopTimer;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
58import static org.onosproject.net.MastershipRole.MASTER;
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -080059import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080060
tom4a5d1712014-09-23 17:49:39 -070061@Component(immediate = true)
62@Service
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -070063public class MastershipManager
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -080064 implements MastershipService, MastershipAdminService, MastershipTermService,
65 MetricsHelper {
Ayaka Koshibe16609692014-09-23 12:46:15 -070066
67 private static final String NODE_ID_NULL = "Node ID cannot be null";
68 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
69 private static final String ROLE_NULL = "Mastership role cannot be null";
70
71 private final Logger log = getLogger(getClass());
72
73 protected final AbstractListenerRegistry<MastershipEvent, MastershipListener>
alshabib339a3d92014-09-26 17:54:32 -070074 listenerRegistry = new AbstractListenerRegistry<>();
75
76 private final MastershipStoreDelegate delegate = new InternalDelegate();
Ayaka Koshibe16609692014-09-23 12:46:15 -070077
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected MastershipStore store;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected EventDeliveryService eventDispatcher;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom4a5d1712014-09-23 17:49:39 -070085 protected ClusterService clusterService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070086
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected MetricsService metricsService;
89
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080090 private Timer requestRoleTimer;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070091
Ayaka Koshibe16609692014-09-23 12:46:15 -070092 @Activate
93 public void activate() {
Yuta HIGUCHI6a462832014-11-23 23:56:03 -080094 requestRoleTimer = createTimer("Mastership", "requestRole", "responseTime");
95
Ayaka Koshibe16609692014-09-23 12:46:15 -070096 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
alshabib339a3d92014-09-26 17:54:32 -070097 store.setDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -070098 log.info("Started");
99 }
100
101 @Deactivate
102 public void deactivate() {
103 eventDispatcher.removeSink(MastershipEvent.class);
alshabib339a3d92014-09-26 17:54:32 -0700104 store.unsetDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -0700105 log.info("Stopped");
106 }
107
Ayaka Koshibe16609692014-09-23 12:46:15 -0700108 @Override
109 public void setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
110 checkNotNull(nodeId, NODE_ID_NULL);
111 checkNotNull(deviceId, DEVICE_ID_NULL);
112 checkNotNull(role, ROLE_NULL);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700113
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700114 MastershipEvent event = null;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700115
116 switch (role) {
117 case MASTER:
118 event = store.setMaster(nodeId, deviceId);
119 break;
120 case STANDBY:
121 event = store.setStandby(nodeId, deviceId);
122 break;
123 case NONE:
124 event = store.relinquishRole(nodeId, deviceId);
125 break;
126 default:
127 log.info("Unknown role; ignoring");
128 return;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700129 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700130
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700131 if (event != null) {
132 post(event);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700133 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700134 }
135
136 @Override
tomb41d1ac2014-09-24 01:51:24 -0700137 public MastershipRole getLocalRole(DeviceId deviceId) {
138 checkNotNull(deviceId, DEVICE_ID_NULL);
139 return store.getRole(clusterService.getLocalNode().id(), deviceId);
140 }
141
142 @Override
143 public void relinquishMastership(DeviceId deviceId) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700144 MastershipEvent event = null;
Ayaka Koshibe1c292d72014-10-08 17:46:07 -0700145 event = store.relinquishRole(
146 clusterService.getLocalNode().id(), deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700147 if (event != null) {
148 post(event);
149 }
tomb41d1ac2014-09-24 01:51:24 -0700150 }
151
152 @Override
153 public MastershipRole requestRoleFor(DeviceId deviceId) {
154 checkNotNull(deviceId, DEVICE_ID_NULL);
Yuta HIGUCHI6a462832014-11-23 23:56:03 -0800155 final Context timer = startTimer(requestRoleTimer);
156 try {
157 return store.requestRole(deviceId);
158 } finally {
159 stopTimer(timer);
160 }
tomb41d1ac2014-09-24 01:51:24 -0700161 }
162
163 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700164 public NodeId getMasterFor(DeviceId deviceId) {
165 checkNotNull(deviceId, DEVICE_ID_NULL);
166 return store.getMaster(deviceId);
167 }
168
169 @Override
170 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
171 checkNotNull(nodeId, NODE_ID_NULL);
172 return store.getDevices(nodeId);
173 }
174
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700175 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700176 public RoleInfo getNodesFor(DeviceId deviceId) {
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700177 checkNotNull(deviceId, DEVICE_ID_NULL);
178 return store.getNodes(deviceId);
179 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700180
181 @Override
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800182 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
183 return store.getTermFor(deviceId);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700184 }
185
Ayaka Koshibe16609692014-09-23 12:46:15 -0700186 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700187 public void addListener(MastershipListener listener) {
188 checkNotNull(listener);
189 listenerRegistry.addListener(listener);
190 }
191
192 @Override
193 public void removeListener(MastershipListener listener) {
194 checkNotNull(listener);
195 listenerRegistry.removeListener(listener);
196 }
197
Yuta HIGUCHIa22f69f2014-11-24 22:25:17 -0800198 @Override
199 public MetricsService metricsService() {
200 return metricsService;
201 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700202
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800203 @Override
204 public void balanceRoles() {
205 List<ControllerNode> nodes = newArrayList(clusterService.getNodes());
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800206 Map<ControllerNode, Set<DeviceId>> controllerDevices = new HashMap<>();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800207 int deviceCount = 0;
208
209 // Create buckets reflecting current ownership.
210 for (ControllerNode node : nodes) {
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800211 if (clusterService.getState(node.id()) == ACTIVE) {
212 Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id()));
213 deviceCount += devicesOf.size();
214 controllerDevices.put(node, devicesOf);
215 log.info("Node {} has {} devices.", node.id(), devicesOf.size());
216 }
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800217 }
218
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800219 // Now re-balance the buckets until they are roughly even.
220 int rounds = controllerDevices.keySet().size();
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800221 for (int i = 0; i < rounds; i++) {
222 // Iterate over the buckets and find the smallest and the largest.
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800223 ControllerNode smallest = findBucket(true, controllerDevices);
224 ControllerNode largest = findBucket(false, controllerDevices);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800225 balanceBuckets(smallest, largest, controllerDevices, deviceCount);
226 }
227 }
228
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800229 private ControllerNode findBucket(boolean min,
230 Map<ControllerNode, Set<DeviceId>> controllerDevices) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800231 int xSize = min ? Integer.MAX_VALUE : -1;
232 ControllerNode xNode = null;
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800233 for (ControllerNode node : controllerDevices.keySet()) {
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800234 int size = controllerDevices.get(node).size();
235 if ((min && size < xSize) || (!min && size > xSize)) {
236 xSize = size;
237 xNode = node;
238 }
239 }
240 return xNode;
241 }
242
243 private void balanceBuckets(ControllerNode smallest, ControllerNode largest,
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800244 Map<ControllerNode, Set<DeviceId>> controllerDevices,
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800245 int deviceCount) {
246 Collection<DeviceId> minBucket = controllerDevices.get(smallest);
247 Collection<DeviceId> maxBucket = controllerDevices.get(largest);
248 int bucketCount = controllerDevices.keySet().size();
249
250 int delta = (maxBucket.size() - minBucket.size()) / 2;
251 delta = Math.min(deviceCount / bucketCount, delta);
252
253 if (delta > 0) {
254 log.info("Attempting to move {} nodes from {} to {}...", delta,
255 largest.id(), smallest.id());
256
257 int i = 0;
258 Iterator<DeviceId> it = maxBucket.iterator();
259 while (it.hasNext() && i < delta) {
260 DeviceId deviceId = it.next();
261 log.info("Setting {} as the master for {}", smallest.id(), deviceId);
262 setRole(smallest.id(), deviceId, MASTER);
Thomas Vachuska12dfdc32014-11-29 16:03:12 -0800263 controllerDevices.get(smallest).add(deviceId);
Thomas Vachuska1e68bdd2014-11-29 13:53:10 -0800264 it.remove();
265 i++;
266 }
267 }
268 }
269
270
Ayaka Koshibe16609692014-09-23 12:46:15 -0700271 // Posts the specified event to the local event dispatcher.
272 private void post(MastershipEvent event) {
273 if (event != null && eventDispatcher != null) {
274 eventDispatcher.post(event);
275 }
276 }
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -0700277
alshabib339a3d92014-09-26 17:54:32 -0700278 public class InternalDelegate implements MastershipStoreDelegate {
279
280 @Override
281 public void notify(MastershipEvent event) {
Yuta HIGUCHI9e11ac02014-11-12 10:09:49 -0800282 log.trace("dispatching mastership event {}", event);
alshabib339a3d92014-09-26 17:54:32 -0700283 eventDispatcher.post(event);
284 }
285
286 }
287
Ayaka Koshibe16609692014-09-23 12:46:15 -0700288}