blob: 04833e60da5f40dbcb32e40896837b0efaf5e3a1 [file] [log] [blame]
tomb41d1ac2014-09-24 01:51:24 -07001package org.onlab.onos.store.cluster.impl;
2
alshabib339a3d92014-09-26 17:54:32 -07003import static org.onlab.onos.cluster.MastershipEvent.Type.MASTER_CHANGED;
4
5import java.util.Map;
alshabib339a3d92014-09-26 17:54:32 -07006import java.util.Set;
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -07007
tomb41d1ac2014-09-24 01:51:24 -07008import org.apache.felix.scr.annotations.Activate;
9import org.apache.felix.scr.annotations.Component;
10import org.apache.felix.scr.annotations.Deactivate;
11import org.apache.felix.scr.annotations.Reference;
12import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -070013import org.apache.felix.scr.annotations.ReferencePolicy;
tomb41d1ac2014-09-24 01:51:24 -070014import org.apache.felix.scr.annotations.Service;
15import org.onlab.onos.cluster.ClusterService;
16import org.onlab.onos.cluster.MastershipEvent;
17import org.onlab.onos.cluster.MastershipStore;
tom0755a362014-09-24 11:54:43 -070018import org.onlab.onos.cluster.MastershipStoreDelegate;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070019import org.onlab.onos.cluster.MastershipTerm;
tomb41d1ac2014-09-24 01:51:24 -070020import org.onlab.onos.cluster.NodeId;
21import org.onlab.onos.net.DeviceId;
22import org.onlab.onos.net.MastershipRole;
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -070023import org.onlab.onos.net.device.DeviceService;
Yuta HIGUCHIb5df76d2014-09-27 20:54:00 -070024import org.onlab.onos.store.common.AbstractHazelcastStore;
tomb41d1ac2014-09-24 01:51:24 -070025
alshabib339a3d92014-09-26 17:54:32 -070026import com.google.common.collect.ImmutableSet;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070027import com.hazelcast.core.ILock;
alshabib339a3d92014-09-26 17:54:32 -070028import com.hazelcast.core.IMap;
tomb41d1ac2014-09-24 01:51:24 -070029
30/**
31 * Distributed implementation of the cluster nodes store.
32 */
33@Component(immediate = true)
34@Service
tom0755a362014-09-24 11:54:43 -070035public class DistributedMastershipStore
Yuta HIGUCHI2e963892014-09-27 13:00:39 -070036extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
alshabib339a3d92014-09-26 17:54:32 -070037implements MastershipStore {
tomb41d1ac2014-09-24 01:51:24 -070038
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070039 //arbitrary lock name
40 private static final String LOCK = "lock";
41 //initial term value
42 private static final Integer INIT = 0;
43 //placeholder non-null value
44 private static final Byte NIL = 0x0;
45
46 //devices to masters
47 protected IMap<byte[], byte[]> rawMasters;
48 //devices to terms
49 protected IMap<byte[], Integer> rawTerms;
50 //collection of nodes. values are ignored, as it's used as a makeshift 'set'
51 protected IMap<byte[], Byte> backups;
52
tomb41d1ac2014-09-24 01:51:24 -070053 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
54 protected ClusterService clusterService;
55
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -070056 //FIXME: need to guarantee that this will be met, sans circular dependencies
57 @Reference(policy = ReferencePolicy.DYNAMIC)
58 protected DeviceService deviceService;
59
Ayaka Koshibe406d0102014-09-24 16:08:12 -070060 @Override
tomb41d1ac2014-09-24 01:51:24 -070061 @Activate
62 public void activate() {
63 super.activate();
64
65 rawMasters = theInstance.getMap("masters");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070066 rawTerms = theInstance.getMap("terms");
67 backups = theInstance.getMap("backups");
tomb41d1ac2014-09-24 01:51:24 -070068
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070069 rawMasters.addEntryListener(new RemoteMasterShipEventHandler(), true);
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -070070
tomb41d1ac2014-09-24 01:51:24 -070071 log.info("Started");
72 }
73
74 @Deactivate
75 public void deactivate() {
76 log.info("Stopped");
77 }
78
79 @Override
Ayaka Koshibe406d0102014-09-24 16:08:12 -070080 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070081 byte [] did = serialize(deviceId);
82 byte [] nid = serialize(nodeId);
tomb41d1ac2014-09-24 01:51:24 -070083
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070084 ILock lock = theInstance.getLock(LOCK);
85 lock.lock();
86 try {
87 MastershipRole role = getRole(nodeId, deviceId);
88 Integer term = rawTerms.get(did);
89 switch (role) {
90 case MASTER:
91 return null;
92 case STANDBY:
93 rawMasters.put(did, nid);
94 rawTerms.put(did, ++term);
95 backups.putIfAbsent(nid, NIL);
96 break;
97 case NONE:
98 rawMasters.put(did, nid);
99 //new switch OR state transition after being orphaned
100 if (term == null) {
101 rawTerms.put(did, INIT);
102 } else {
103 rawTerms.put(did, ++term);
104 }
105 backups.put(nid, NIL);
106 break;
107 default:
108 log.warn("unknown Mastership Role {}", role);
109 return null;
110 }
111 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
112 } finally {
113 lock.unlock();
tomb41d1ac2014-09-24 01:51:24 -0700114 }
115 }
116
117 @Override
118 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700119 return deserialize(rawMasters.get(serialize(deviceId)));
tomb41d1ac2014-09-24 01:51:24 -0700120 }
121
122 @Override
123 public Set<DeviceId> getDevices(NodeId nodeId) {
124 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700125
126 for (Map.Entry<byte[], byte[]> entry : rawMasters.entrySet()) {
127 if (nodeId.equals(deserialize(entry.getValue()))) {
128 builder.add((DeviceId) deserialize(entry.getKey()));
tomb41d1ac2014-09-24 01:51:24 -0700129 }
130 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700131
tomb41d1ac2014-09-24 01:51:24 -0700132 return builder.build();
133 }
134
135 @Override
136 public MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700137 // first to empty slot for device in master map is MASTER
138 // depending on how backups are organized, might need to trigger election
139 // so only controller doesn't set itself to backup for another device
140 byte [] did = serialize(deviceId);
141 NodeId local = clusterService.getLocalNode().id();
142 byte [] lnid = serialize(local);
143
144 ILock lock = theInstance.getLock(LOCK);
145 lock.lock();
146 try {
147 MastershipRole role = getRole(local, deviceId);
148 switch (role) {
149 case MASTER:
150 break;
151 case STANDBY:
152 backups.put(lnid, NIL);
153 rawTerms.putIfAbsent(did, INIT);
154 break;
155 case NONE:
156 rawMasters.put(did, lnid);
157 rawTerms.putIfAbsent(did, INIT);
158 backups.put(lnid, NIL);
159 role = MastershipRole.MASTER;
160 break;
161 default:
162 log.warn("unknown Mastership Role {}", role);
163 }
164 return role;
165 } finally {
166 lock.unlock();
167 }
tomb41d1ac2014-09-24 01:51:24 -0700168 }
169
170 @Override
171 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700172 byte[] did = serialize(deviceId);
173
174 NodeId current = deserialize(rawMasters.get(did));
175 MastershipRole role = null;
176
177 if (current == null) {
178 //IFF no controllers have claimed mastership over it
179 role = MastershipRole.NONE;
180 } else {
181 if (current.equals(nodeId)) {
182 role = MastershipRole.MASTER;
183 } else {
184 role = MastershipRole.STANDBY;
185 }
186 }
187
188 return role;
tomb41d1ac2014-09-24 01:51:24 -0700189 }
190
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700191 @Override
192 public MastershipTerm getTermFor(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700193 byte[] did = serialize(deviceId);
194
195 if ((rawMasters.get(did) == null) ||
196 (rawTerms.get(did) == null)) {
197 return null;
198 }
199 return MastershipTerm.of(
200 (NodeId) deserialize(rawMasters.get(did)), rawTerms.get(did));
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700201 }
202
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700203 @Override
204 public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700205 byte [] did = serialize(deviceId);
206
207 ILock lock = theInstance.getLock(LOCK);
208 lock.lock();
209 try {
210 MastershipRole role = getRole(nodeId, deviceId);
211 switch (role) {
212 case MASTER:
213 //hand off device to another
214 NodeId backup = reelect(nodeId, deviceId);
215 if (backup == null) {
216 //goes back to NONE
217 rawMasters.remove(did);
218 } else {
219 //goes to STANDBY for local, MASTER for someone else
220 Integer term = rawTerms.get(did);
221 rawMasters.put(did, serialize(backup));
222 rawTerms.put(did, ++term);
223 return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
224 }
225 case STANDBY:
226 case NONE:
227 break;
228 default:
229 log.warn("unknown Mastership Role {}", role);
230 }
231 return null;
232 } finally {
233 lock.unlock();
234 }
235 }
236
237 //helper for "re-electing" a new master for a given device
238 private NodeId reelect(NodeId current, DeviceId deviceId) {
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700239
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700240 for (byte [] node : backups.keySet()) {
241 NodeId nid = deserialize(node);
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700242 //if a device dies we shouldn't pick another master for it.
243 if (!current.equals(nid) && (deviceService.isAvailable(deviceId))) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700244 return nid;
245 }
246 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700247 return null;
248 }
249
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700250 //adds node to pool(s) of backup
251 private void backup(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700252 //TODO might be useful to isolate out this function and reelect() if we
253 //get more backup/election schemes
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700254 }
255
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700256 private class RemoteMasterShipEventHandler extends RemoteEventHandler<DeviceId, NodeId> {
alshabib339a3d92014-09-26 17:54:32 -0700257
258 @Override
259 protected void onAdd(DeviceId deviceId, NodeId nodeId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700260 //only addition indicates a change in mastership
alshabib339a3d92014-09-26 17:54:32 -0700261 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
262 }
263
264 @Override
265 protected void onRemove(DeviceId deviceId, NodeId nodeId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700266 //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
alshabib339a3d92014-09-26 17:54:32 -0700267 }
268
269 @Override
270 protected void onUpdate(DeviceId deviceId, NodeId oldNodeId, NodeId nodeId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700271 //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
alshabib339a3d92014-09-26 17:54:32 -0700272 }
273 }
274
tomb41d1ac2014-09-24 01:51:24 -0700275}