blob: e97a553902f7320985273f7965efb4fa4ecbf002 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
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 */
Ayaka Koshibe16609692014-09-23 12:46:15 -070016package org.onlab.onos.cluster.impl;
17
alshabib339a3d92014-09-26 17:54:32 -070018import static com.google.common.base.Preconditions.checkNotNull;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import java.util.Set;
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -070022import java.util.concurrent.atomic.AtomicInteger;
alshabib339a3d92014-09-26 17:54:32 -070023
Ayaka Koshibe16609692014-09-23 12:46:15 -070024import org.apache.felix.scr.annotations.Activate;
tom4a5d1712014-09-23 17:49:39 -070025import org.apache.felix.scr.annotations.Component;
Ayaka Koshibe16609692014-09-23 12:46:15 -070026import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
tom4a5d1712014-09-23 17:49:39 -070029import org.apache.felix.scr.annotations.Service;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070030import org.onlab.onos.cluster.ClusterEvent;
31import org.onlab.onos.cluster.ClusterEventListener;
tom4a5d1712014-09-23 17:49:39 -070032import org.onlab.onos.cluster.ClusterService;
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -070033import org.onlab.onos.cluster.ControllerNode;
Ayaka Koshibe16609692014-09-23 12:46:15 -070034import org.onlab.onos.cluster.NodeId;
Ayaka Koshibeabedb092014-10-20 17:01:31 -070035import org.onlab.onos.cluster.RoleInfo;
Ayaka Koshibe16609692014-09-23 12:46:15 -070036import org.onlab.onos.event.AbstractListenerRegistry;
37import org.onlab.onos.event.EventDeliveryService;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070038import org.onlab.onos.mastership.MastershipAdminService;
39import org.onlab.onos.mastership.MastershipEvent;
40import org.onlab.onos.mastership.MastershipListener;
41import org.onlab.onos.mastership.MastershipService;
42import org.onlab.onos.mastership.MastershipStore;
43import org.onlab.onos.mastership.MastershipStoreDelegate;
44import org.onlab.onos.mastership.MastershipTerm;
45import org.onlab.onos.mastership.MastershipTermService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070046import org.onlab.onos.net.DeviceId;
47import org.onlab.onos.net.MastershipRole;
Ayaka Koshibe16609692014-09-23 12:46:15 -070048import org.slf4j.Logger;
49
tom4a5d1712014-09-23 17:49:39 -070050@Component(immediate = true)
51@Service
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -070052public class MastershipManager
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -080053 implements MastershipService, MastershipAdminService, MastershipTermService {
Ayaka Koshibe16609692014-09-23 12:46:15 -070054
55 private static final String NODE_ID_NULL = "Node ID cannot be null";
56 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
57 private static final String ROLE_NULL = "Mastership role cannot be null";
58
59 private final Logger log = getLogger(getClass());
60
61 protected final AbstractListenerRegistry<MastershipEvent, MastershipListener>
alshabib339a3d92014-09-26 17:54:32 -070062 listenerRegistry = new AbstractListenerRegistry<>();
63
64 private final MastershipStoreDelegate delegate = new InternalDelegate();
Ayaka Koshibe16609692014-09-23 12:46:15 -070065
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected MastershipStore store;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected EventDeliveryService eventDispatcher;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom4a5d1712014-09-23 17:49:39 -070073 protected ClusterService clusterService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070074
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070075 private ClusterEventListener clusterListener = new InternalClusterEventListener();
76
Ayaka Koshibe16609692014-09-23 12:46:15 -070077 @Activate
78 public void activate() {
79 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070080 clusterService.addListener(clusterListener);
alshabib339a3d92014-09-26 17:54:32 -070081 store.setDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -070082 log.info("Started");
83 }
84
85 @Deactivate
86 public void deactivate() {
87 eventDispatcher.removeSink(MastershipEvent.class);
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070088 clusterService.removeListener(clusterListener);
alshabib339a3d92014-09-26 17:54:32 -070089 store.unsetDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -070090 log.info("Stopped");
91 }
92
Ayaka Koshibe16609692014-09-23 12:46:15 -070093 @Override
94 public void setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
95 checkNotNull(nodeId, NODE_ID_NULL);
96 checkNotNull(deviceId, DEVICE_ID_NULL);
97 checkNotNull(role, ROLE_NULL);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070098
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070099 MastershipEvent event = null;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700100
101 switch (role) {
102 case MASTER:
103 event = store.setMaster(nodeId, deviceId);
104 break;
105 case STANDBY:
106 event = store.setStandby(nodeId, deviceId);
107 break;
108 case NONE:
109 event = store.relinquishRole(nodeId, deviceId);
110 break;
111 default:
112 log.info("Unknown role; ignoring");
113 return;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700114 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700115
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700116 if (event != null) {
117 post(event);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700118 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700119 }
120
121 @Override
tomb41d1ac2014-09-24 01:51:24 -0700122 public MastershipRole getLocalRole(DeviceId deviceId) {
123 checkNotNull(deviceId, DEVICE_ID_NULL);
124 return store.getRole(clusterService.getLocalNode().id(), deviceId);
125 }
126
127 @Override
128 public void relinquishMastership(DeviceId deviceId) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700129 MastershipEvent event = null;
Ayaka Koshibe1c292d72014-10-08 17:46:07 -0700130 event = store.relinquishRole(
131 clusterService.getLocalNode().id(), deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700132 if (event != null) {
133 post(event);
134 }
tomb41d1ac2014-09-24 01:51:24 -0700135 }
136
137 @Override
138 public MastershipRole requestRoleFor(DeviceId deviceId) {
139 checkNotNull(deviceId, DEVICE_ID_NULL);
140 return store.requestRole(deviceId);
141 }
142
143 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700144 public NodeId getMasterFor(DeviceId deviceId) {
145 checkNotNull(deviceId, DEVICE_ID_NULL);
146 return store.getMaster(deviceId);
147 }
148
149 @Override
150 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
151 checkNotNull(nodeId, NODE_ID_NULL);
152 return store.getDevices(nodeId);
153 }
154
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700155 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700156 public RoleInfo getNodesFor(DeviceId deviceId) {
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700157 checkNotNull(deviceId, DEVICE_ID_NULL);
158 return store.getNodes(deviceId);
159 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700160
161 @Override
Yuta HIGUCHIbcac4992014-11-22 19:27:57 -0800162 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
163 return store.getTermFor(deviceId);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700164 }
165
Ayaka Koshibe16609692014-09-23 12:46:15 -0700166 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700167 public void addListener(MastershipListener listener) {
168 checkNotNull(listener);
169 listenerRegistry.addListener(listener);
170 }
171
172 @Override
173 public void removeListener(MastershipListener listener) {
174 checkNotNull(listener);
175 listenerRegistry.removeListener(listener);
176 }
177
tomb41d1ac2014-09-24 01:51:24 -0700178 // FIXME: provide wiring to allow events to be triggered by changes within the store
Ayaka Koshibe16609692014-09-23 12:46:15 -0700179
180 // Posts the specified event to the local event dispatcher.
181 private void post(MastershipEvent event) {
182 if (event != null && eventDispatcher != null) {
183 eventDispatcher.post(event);
184 }
185 }
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -0700186
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700187
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700188
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700189 //callback for reacting to cluster events
190 private class InternalClusterEventListener implements ClusterEventListener {
191
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700192 // A notion of a local maximum cluster size, used to tie-break.
193 // Think of a better way to do this.
194 private AtomicInteger clusterSize;
195
196 InternalClusterEventListener() {
197 clusterSize = new AtomicInteger(0);
198 }
199
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700200 @Override
201 public void event(ClusterEvent event) {
202 switch (event.type()) {
203 //FIXME: worry about addition when the time comes
204 case INSTANCE_ADDED:
205 case INSTANCE_ACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700206 clusterSize.incrementAndGet();
207 log.info("instance {} added/activated", event.subject());
208 break;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700209 case INSTANCE_REMOVED:
210 case INSTANCE_DEACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700211 ControllerNode node = event.subject();
212
213 if (node.equals(clusterService.getLocalNode())) {
214 //If we are in smaller cluster, relinquish and return
215 for (DeviceId device : getDevicesOf(node.id())) {
216 if (!isInMajority()) {
217 //own DeviceManager should catch event and tell switch
218 store.relinquishRole(node.id(), device);
219 }
220 }
221 log.info("broke off from cluster, relinquished devices");
222 break;
223 }
224
225 // if we are the larger one and the removed node(s) are brain dead,
226 // force relinquish on behalf of disabled node.
227 // check network channel to do this?
228 for (DeviceId device : getDevicesOf(node.id())) {
229 //some things to check:
230 // 1. we didn't break off as well while we're at it
231 // 2. others don't pile in and try too - maybe a lock
232 if (isInMajority()) {
233 store.relinquishRole(node.id(), device);
234 }
235 }
236 clusterSize.decrementAndGet();
237 log.info("instance {} removed/deactivated", event.subject());
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700238 break;
239 default:
240 log.warn("unknown cluster event {}", event);
241 }
242 }
243
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700244 private boolean isInMajority() {
245 if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
246 return true;
247 }
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700248// else {
Ayaka Koshibe67af1f42014-10-20 15:26:37 -0700249 //FIXME: break tie for equal-sized clusters, by number of
250 // connected switches, then masters, then nodeId hash
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700251 // problem is, how do we get at channel info cleanly here?
252 // Also, what's the time hit for a distributed store look-up
253 // versus channel re-negotiation? bet on the latter being worse.
254
255// }
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700256 return false;
257 }
258
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700259 }
Ayaka Koshibe65efaef2014-09-29 18:21:56 -0700260
alshabib339a3d92014-09-26 17:54:32 -0700261 public class InternalDelegate implements MastershipStoreDelegate {
262
263 @Override
264 public void notify(MastershipEvent event) {
Yuta HIGUCHI9e11ac02014-11-12 10:09:49 -0800265 log.trace("dispatching mastership event {}", event);
alshabib339a3d92014-09-26 17:54:32 -0700266 eventDispatcher.post(event);
267 }
268
269 }
270
Ayaka Koshibe16609692014-09-23 12:46:15 -0700271}