blob: 6180ada193e8c86f031c8b2d60a281e8dfd90eb3 [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
alshabib339a3d92014-09-26 17:54:32 -070053implements MastershipService, MastershipAdminService {
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
162 public MastershipTermService requestTermService() {
163 return new InternalMastershipTermService();
164 }
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 private class InternalMastershipTermService implements MastershipTermService {
188
189 @Override
190 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
191 return store.getTermFor(deviceId);
192 }
193
194 }
195
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700196 //callback for reacting to cluster events
197 private class InternalClusterEventListener implements ClusterEventListener {
198
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700199 // A notion of a local maximum cluster size, used to tie-break.
200 // Think of a better way to do this.
201 private AtomicInteger clusterSize;
202
203 InternalClusterEventListener() {
204 clusterSize = new AtomicInteger(0);
205 }
206
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700207 @Override
208 public void event(ClusterEvent event) {
209 switch (event.type()) {
210 //FIXME: worry about addition when the time comes
211 case INSTANCE_ADDED:
212 case INSTANCE_ACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700213 clusterSize.incrementAndGet();
214 log.info("instance {} added/activated", event.subject());
215 break;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700216 case INSTANCE_REMOVED:
217 case INSTANCE_DEACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700218 ControllerNode node = event.subject();
219
220 if (node.equals(clusterService.getLocalNode())) {
221 //If we are in smaller cluster, relinquish and return
222 for (DeviceId device : getDevicesOf(node.id())) {
223 if (!isInMajority()) {
224 //own DeviceManager should catch event and tell switch
225 store.relinquishRole(node.id(), device);
226 }
227 }
228 log.info("broke off from cluster, relinquished devices");
229 break;
230 }
231
232 // if we are the larger one and the removed node(s) are brain dead,
233 // force relinquish on behalf of disabled node.
234 // check network channel to do this?
235 for (DeviceId device : getDevicesOf(node.id())) {
236 //some things to check:
237 // 1. we didn't break off as well while we're at it
238 // 2. others don't pile in and try too - maybe a lock
239 if (isInMajority()) {
240 store.relinquishRole(node.id(), device);
241 }
242 }
243 clusterSize.decrementAndGet();
244 log.info("instance {} removed/deactivated", event.subject());
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700245 break;
246 default:
247 log.warn("unknown cluster event {}", event);
248 }
249 }
250
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700251 private boolean isInMajority() {
252 if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
253 return true;
254 }
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700255// else {
Ayaka Koshibe67af1f42014-10-20 15:26:37 -0700256 //FIXME: break tie for equal-sized clusters, by number of
257 // connected switches, then masters, then nodeId hash
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700258 // problem is, how do we get at channel info cleanly here?
259 // Also, what's the time hit for a distributed store look-up
260 // versus channel re-negotiation? bet on the latter being worse.
261
262// }
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700263 return false;
264 }
265
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700266 }
Ayaka Koshibe65efaef2014-09-29 18:21:56 -0700267
alshabib339a3d92014-09-26 17:54:32 -0700268 public class InternalDelegate implements MastershipStoreDelegate {
269
270 @Override
271 public void notify(MastershipEvent event) {
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700272 if (clusterService.getLocalNode().id().equals(event.roleInfo().master())) {
273 log.info("ignoring locally-generated event {}", event);
274 // return;
275 }
alshabib339a3d92014-09-26 17:54:32 -0700276 log.info("dispatching mastership event {}", event);
277 eventDispatcher.post(event);
278 }
279
280 }
281
Ayaka Koshibe16609692014-09-23 12:46:15 -0700282}