blob: d3cce229f801d040832bbe87cf5752bdf4a25b59 [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;
100 if (role.equals(MastershipRole.MASTER)) {
101 event = store.setMaster(nodeId, deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700102 } else {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700103 event = store.setStandby(nodeId, deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700104 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700105
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700106 if (event != null) {
107 post(event);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700108 }
Ayaka Koshibe16609692014-09-23 12:46:15 -0700109 }
110
111 @Override
tomb41d1ac2014-09-24 01:51:24 -0700112 public MastershipRole getLocalRole(DeviceId deviceId) {
113 checkNotNull(deviceId, DEVICE_ID_NULL);
114 return store.getRole(clusterService.getLocalNode().id(), deviceId);
115 }
116
117 @Override
118 public void relinquishMastership(DeviceId deviceId) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700119 MastershipEvent event = null;
Ayaka Koshibe1c292d72014-10-08 17:46:07 -0700120 event = store.relinquishRole(
121 clusterService.getLocalNode().id(), deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700122 if (event != null) {
123 post(event);
124 }
tomb41d1ac2014-09-24 01:51:24 -0700125 }
126
127 @Override
128 public MastershipRole requestRoleFor(DeviceId deviceId) {
129 checkNotNull(deviceId, DEVICE_ID_NULL);
130 return store.requestRole(deviceId);
131 }
132
133 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700134 public NodeId getMasterFor(DeviceId deviceId) {
135 checkNotNull(deviceId, DEVICE_ID_NULL);
136 return store.getMaster(deviceId);
137 }
138
139 @Override
140 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
141 checkNotNull(nodeId, NODE_ID_NULL);
142 return store.getDevices(nodeId);
143 }
144
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700145 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700146 public RoleInfo getNodesFor(DeviceId deviceId) {
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700147 checkNotNull(deviceId, DEVICE_ID_NULL);
148 return store.getNodes(deviceId);
149 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700150
151 @Override
152 public MastershipTermService requestTermService() {
153 return new InternalMastershipTermService();
154 }
155
Ayaka Koshibe16609692014-09-23 12:46:15 -0700156 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700157 public void addListener(MastershipListener listener) {
158 checkNotNull(listener);
159 listenerRegistry.addListener(listener);
160 }
161
162 @Override
163 public void removeListener(MastershipListener listener) {
164 checkNotNull(listener);
165 listenerRegistry.removeListener(listener);
166 }
167
tomb41d1ac2014-09-24 01:51:24 -0700168 // FIXME: provide wiring to allow events to be triggered by changes within the store
Ayaka Koshibe16609692014-09-23 12:46:15 -0700169
170 // Posts the specified event to the local event dispatcher.
171 private void post(MastershipEvent event) {
172 if (event != null && eventDispatcher != null) {
173 eventDispatcher.post(event);
174 }
175 }
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -0700176
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700177 private class InternalMastershipTermService implements MastershipTermService {
178
179 @Override
180 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
181 return store.getTermFor(deviceId);
182 }
183
184 }
185
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700186 //callback for reacting to cluster events
187 private class InternalClusterEventListener implements ClusterEventListener {
188
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700189 // A notion of a local maximum cluster size, used to tie-break.
190 // Think of a better way to do this.
191 private AtomicInteger clusterSize;
192
193 InternalClusterEventListener() {
194 clusterSize = new AtomicInteger(0);
195 }
196
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700197 @Override
198 public void event(ClusterEvent event) {
199 switch (event.type()) {
200 //FIXME: worry about addition when the time comes
201 case INSTANCE_ADDED:
202 case INSTANCE_ACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700203 clusterSize.incrementAndGet();
204 log.info("instance {} added/activated", event.subject());
205 break;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700206 case INSTANCE_REMOVED:
207 case INSTANCE_DEACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700208 ControllerNode node = event.subject();
209
210 if (node.equals(clusterService.getLocalNode())) {
211 //If we are in smaller cluster, relinquish and return
212 for (DeviceId device : getDevicesOf(node.id())) {
213 if (!isInMajority()) {
214 //own DeviceManager should catch event and tell switch
215 store.relinquishRole(node.id(), device);
216 }
217 }
218 log.info("broke off from cluster, relinquished devices");
219 break;
220 }
221
222 // if we are the larger one and the removed node(s) are brain dead,
223 // force relinquish on behalf of disabled node.
224 // check network channel to do this?
225 for (DeviceId device : getDevicesOf(node.id())) {
226 //some things to check:
227 // 1. we didn't break off as well while we're at it
228 // 2. others don't pile in and try too - maybe a lock
229 if (isInMajority()) {
230 store.relinquishRole(node.id(), device);
231 }
232 }
233 clusterSize.decrementAndGet();
234 log.info("instance {} removed/deactivated", event.subject());
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700235 break;
236 default:
237 log.warn("unknown cluster event {}", event);
238 }
239 }
240
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700241 private boolean isInMajority() {
242 if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
243 return true;
244 }
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700245// else {
Ayaka Koshibe67af1f42014-10-20 15:26:37 -0700246 //FIXME: break tie for equal-sized clusters, by number of
247 // connected switches, then masters, then nodeId hash
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700248 // problem is, how do we get at channel info cleanly here?
249 // Also, what's the time hit for a distributed store look-up
250 // versus channel re-negotiation? bet on the latter being worse.
251
252// }
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700253 return false;
254 }
255
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700256 }
Ayaka Koshibe65efaef2014-09-29 18:21:56 -0700257
alshabib339a3d92014-09-26 17:54:32 -0700258 public class InternalDelegate implements MastershipStoreDelegate {
259
260 @Override
261 public void notify(MastershipEvent event) {
262 log.info("dispatching mastership event {}", event);
263 eventDispatcher.post(event);
264 }
265
266 }
267
Ayaka Koshibe16609692014-09-23 12:46:15 -0700268}