blob: 4bcaff4a1e4dead5500ea9783bad06c9f1f62d62 [file] [log] [blame]
Ayaka Koshibe16609692014-09-23 12:46:15 -07001package org.onlab.onos.cluster.impl;
2
alshabib339a3d92014-09-26 17:54:32 -07003import static com.google.common.base.Preconditions.checkNotNull;
4import static org.slf4j.LoggerFactory.getLogger;
5
Ayaka Koshibe45503ce2014-10-14 11:26:45 -07006import java.util.List;
alshabib339a3d92014-09-26 17:54:32 -07007import java.util.Set;
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -07008import java.util.concurrent.atomic.AtomicInteger;
alshabib339a3d92014-09-26 17:54:32 -07009
Ayaka Koshibe16609692014-09-23 12:46:15 -070010import org.apache.felix.scr.annotations.Activate;
tom4a5d1712014-09-23 17:49:39 -070011import org.apache.felix.scr.annotations.Component;
Ayaka Koshibe16609692014-09-23 12:46:15 -070012import org.apache.felix.scr.annotations.Deactivate;
13import org.apache.felix.scr.annotations.Reference;
14import org.apache.felix.scr.annotations.ReferenceCardinality;
tom4a5d1712014-09-23 17:49:39 -070015import org.apache.felix.scr.annotations.Service;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070016import org.onlab.onos.cluster.ClusterEvent;
17import org.onlab.onos.cluster.ClusterEventListener;
tom4a5d1712014-09-23 17:49:39 -070018import org.onlab.onos.cluster.ClusterService;
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -070019import org.onlab.onos.cluster.ControllerNode;
Ayaka Koshibe16609692014-09-23 12:46:15 -070020import org.onlab.onos.cluster.NodeId;
21import org.onlab.onos.event.AbstractListenerRegistry;
22import org.onlab.onos.event.EventDeliveryService;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070023import org.onlab.onos.mastership.MastershipAdminService;
24import org.onlab.onos.mastership.MastershipEvent;
25import org.onlab.onos.mastership.MastershipListener;
26import org.onlab.onos.mastership.MastershipService;
27import org.onlab.onos.mastership.MastershipStore;
28import org.onlab.onos.mastership.MastershipStoreDelegate;
29import org.onlab.onos.mastership.MastershipTerm;
30import org.onlab.onos.mastership.MastershipTermService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070031import org.onlab.onos.net.DeviceId;
32import org.onlab.onos.net.MastershipRole;
Ayaka Koshibe16609692014-09-23 12:46:15 -070033import org.slf4j.Logger;
34
tom4a5d1712014-09-23 17:49:39 -070035@Component(immediate = true)
36@Service
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -070037public class MastershipManager
alshabib339a3d92014-09-26 17:54:32 -070038implements MastershipService, MastershipAdminService {
Ayaka Koshibe16609692014-09-23 12:46:15 -070039
40 private static final String NODE_ID_NULL = "Node ID cannot be null";
41 private static final String DEVICE_ID_NULL = "Device ID cannot be null";
42 private static final String ROLE_NULL = "Mastership role cannot be null";
43
44 private final Logger log = getLogger(getClass());
45
46 protected final AbstractListenerRegistry<MastershipEvent, MastershipListener>
alshabib339a3d92014-09-26 17:54:32 -070047 listenerRegistry = new AbstractListenerRegistry<>();
48
49 private final MastershipStoreDelegate delegate = new InternalDelegate();
Ayaka Koshibe16609692014-09-23 12:46:15 -070050
51 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
52 protected MastershipStore store;
53
54 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
55 protected EventDeliveryService eventDispatcher;
56
57 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom4a5d1712014-09-23 17:49:39 -070058 protected ClusterService clusterService;
Ayaka Koshibe16609692014-09-23 12:46:15 -070059
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070060 private ClusterEventListener clusterListener = new InternalClusterEventListener();
61
Ayaka Koshibe16609692014-09-23 12:46:15 -070062 @Activate
63 public void activate() {
64 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070065 clusterService.addListener(clusterListener);
alshabib339a3d92014-09-26 17:54:32 -070066 store.setDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -070067 log.info("Started");
68 }
69
70 @Deactivate
71 public void deactivate() {
72 eventDispatcher.removeSink(MastershipEvent.class);
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070073 clusterService.removeListener(clusterListener);
alshabib339a3d92014-09-26 17:54:32 -070074 store.unsetDelegate(delegate);
Ayaka Koshibe16609692014-09-23 12:46:15 -070075 log.info("Stopped");
76 }
77
Ayaka Koshibe16609692014-09-23 12:46:15 -070078 @Override
79 public void setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
80 checkNotNull(nodeId, NODE_ID_NULL);
81 checkNotNull(deviceId, DEVICE_ID_NULL);
82 checkNotNull(role, ROLE_NULL);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070083
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070084 MastershipEvent event = null;
85 if (role.equals(MastershipRole.MASTER)) {
86 event = store.setMaster(nodeId, deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070087 } else {
Ayaka Koshibec4047702014-10-07 14:43:52 -070088 event = store.setStandby(nodeId, deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070089 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070090
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070091 if (event != null) {
92 post(event);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070093 }
Ayaka Koshibe16609692014-09-23 12:46:15 -070094 }
95
96 @Override
tomb41d1ac2014-09-24 01:51:24 -070097 public MastershipRole getLocalRole(DeviceId deviceId) {
98 checkNotNull(deviceId, DEVICE_ID_NULL);
99 return store.getRole(clusterService.getLocalNode().id(), deviceId);
100 }
101
102 @Override
103 public void relinquishMastership(DeviceId deviceId) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700104 MastershipEvent event = null;
Ayaka Koshibe1c292d72014-10-08 17:46:07 -0700105 event = store.relinquishRole(
106 clusterService.getLocalNode().id(), deviceId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700107
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700108 if (event != null) {
109 post(event);
110 }
tomb41d1ac2014-09-24 01:51:24 -0700111 }
112
113 @Override
114 public MastershipRole requestRoleFor(DeviceId deviceId) {
115 checkNotNull(deviceId, DEVICE_ID_NULL);
116 return store.requestRole(deviceId);
117 }
118
119 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700120 public NodeId getMasterFor(DeviceId deviceId) {
121 checkNotNull(deviceId, DEVICE_ID_NULL);
122 return store.getMaster(deviceId);
123 }
124
125 @Override
126 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
127 checkNotNull(nodeId, NODE_ID_NULL);
128 return store.getDevices(nodeId);
129 }
130
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700131 @Override
132 public List<NodeId> getNodesFor(DeviceId deviceId) {
133 checkNotNull(deviceId, DEVICE_ID_NULL);
134 return store.getNodes(deviceId);
135 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700136
137 @Override
138 public MastershipTermService requestTermService() {
139 return new InternalMastershipTermService();
140 }
141
Ayaka Koshibe16609692014-09-23 12:46:15 -0700142 @Override
Ayaka Koshibe16609692014-09-23 12:46:15 -0700143 public void addListener(MastershipListener listener) {
144 checkNotNull(listener);
145 listenerRegistry.addListener(listener);
146 }
147
148 @Override
149 public void removeListener(MastershipListener listener) {
150 checkNotNull(listener);
151 listenerRegistry.removeListener(listener);
152 }
153
tomb41d1ac2014-09-24 01:51:24 -0700154 // FIXME: provide wiring to allow events to be triggered by changes within the store
Ayaka Koshibe16609692014-09-23 12:46:15 -0700155
156 // Posts the specified event to the local event dispatcher.
157 private void post(MastershipEvent event) {
158 if (event != null && eventDispatcher != null) {
159 eventDispatcher.post(event);
160 }
161 }
Ayaka Koshibe3eed2b02014-09-23 13:28:05 -0700162
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700163 private class InternalMastershipTermService implements MastershipTermService {
164
165 @Override
166 public MastershipTerm getMastershipTerm(DeviceId deviceId) {
167 return store.getTermFor(deviceId);
168 }
169
170 }
171
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700172 //callback for reacting to cluster events
173 private class InternalClusterEventListener implements ClusterEventListener {
174
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700175 // A notion of a local maximum cluster size, used to tie-break.
176 // Think of a better way to do this.
177 private AtomicInteger clusterSize;
178
179 InternalClusterEventListener() {
180 clusterSize = new AtomicInteger(0);
181 }
182
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700183 @Override
184 public void event(ClusterEvent event) {
185 switch (event.type()) {
186 //FIXME: worry about addition when the time comes
187 case INSTANCE_ADDED:
188 case INSTANCE_ACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700189 clusterSize.incrementAndGet();
190 log.info("instance {} added/activated", event.subject());
191 break;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700192 case INSTANCE_REMOVED:
193 case INSTANCE_DEACTIVATED:
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700194 ControllerNode node = event.subject();
195
196 if (node.equals(clusterService.getLocalNode())) {
197 //If we are in smaller cluster, relinquish and return
198 for (DeviceId device : getDevicesOf(node.id())) {
199 if (!isInMajority()) {
200 //own DeviceManager should catch event and tell switch
201 store.relinquishRole(node.id(), device);
202 }
203 }
204 log.info("broke off from cluster, relinquished devices");
205 break;
206 }
207
208 // if we are the larger one and the removed node(s) are brain dead,
209 // force relinquish on behalf of disabled node.
210 // check network channel to do this?
211 for (DeviceId device : getDevicesOf(node.id())) {
212 //some things to check:
213 // 1. we didn't break off as well while we're at it
214 // 2. others don't pile in and try too - maybe a lock
215 if (isInMajority()) {
216 store.relinquishRole(node.id(), device);
217 }
218 }
219 clusterSize.decrementAndGet();
220 log.info("instance {} removed/deactivated", event.subject());
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700221 break;
222 default:
223 log.warn("unknown cluster event {}", event);
224 }
225 }
226
Ayaka Koshibeea5b4ce2014-10-11 14:17:17 -0700227 private boolean isInMajority() {
228 if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
229 return true;
230 }
231 //else {
232 //FIXME: break tie for equal-sized clusters, can we use hz's functions?
233 // }
234 return false;
235 }
236
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700237 }
Ayaka Koshibe65efaef2014-09-29 18:21:56 -0700238
alshabib339a3d92014-09-26 17:54:32 -0700239 public class InternalDelegate implements MastershipStoreDelegate {
240
241 @Override
242 public void notify(MastershipEvent event) {
243 log.info("dispatching mastership event {}", event);
244 eventDispatcher.post(event);
245 }
246
247 }
248
Ayaka Koshibe16609692014-09-23 12:46:15 -0700249}