blob: dc427739a49bd490be52d22e62887d05f6af4930 [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 com.google.common.cache.CacheBuilder.newBuilder;
4import static org.onlab.onos.cluster.MastershipEvent.Type.MASTER_CHANGED;
5
6import java.util.Map;
alshabib339a3d92014-09-26 17:54:32 -07007import java.util.Set;
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -07008
tomb41d1ac2014-09-24 01:51:24 -07009import org.apache.felix.scr.annotations.Activate;
10import org.apache.felix.scr.annotations.Component;
11import org.apache.felix.scr.annotations.Deactivate;
12import org.apache.felix.scr.annotations.Reference;
13import org.apache.felix.scr.annotations.ReferenceCardinality;
14import 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;
Yuta HIGUCHIb5df76d2014-09-27 20:54:00 -070023import org.onlab.onos.store.common.AbsentInvalidatingLoadingCache;
24import org.onlab.onos.store.common.AbstractHazelcastStore;
25import org.onlab.onos.store.common.OptionalCacheLoader;
tomb41d1ac2014-09-24 01:51:24 -070026
alshabib339a3d92014-09-26 17:54:32 -070027import com.google.common.base.Optional;
28import com.google.common.cache.LoadingCache;
29import com.google.common.collect.ImmutableSet;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070030import com.hazelcast.core.ILock;
alshabib339a3d92014-09-26 17:54:32 -070031import com.hazelcast.core.IMap;
tomb41d1ac2014-09-24 01:51:24 -070032
33/**
34 * Distributed implementation of the cluster nodes store.
35 */
36@Component(immediate = true)
37@Service
tom0755a362014-09-24 11:54:43 -070038public class DistributedMastershipStore
Yuta HIGUCHI2e963892014-09-27 13:00:39 -070039extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
alshabib339a3d92014-09-26 17:54:32 -070040implements MastershipStore {
tomb41d1ac2014-09-24 01:51:24 -070041
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070042 //arbitrary lock name
43 private static final String LOCK = "lock";
44 //initial term value
45 private static final Integer INIT = 0;
46 //placeholder non-null value
47 private static final Byte NIL = 0x0;
48
49 //devices to masters
50 protected IMap<byte[], byte[]> rawMasters;
51 //devices to terms
52 protected IMap<byte[], Integer> rawTerms;
53 //collection of nodes. values are ignored, as it's used as a makeshift 'set'
54 protected IMap<byte[], Byte> backups;
55
56 //TODO - remove
57 //private LoadingCache<DeviceId, Optional<NodeId>> masters;
tomb41d1ac2014-09-24 01:51:24 -070058
59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
60 protected ClusterService clusterService;
61
Ayaka Koshibe406d0102014-09-24 16:08:12 -070062 @Override
tomb41d1ac2014-09-24 01:51:24 -070063 @Activate
64 public void activate() {
65 super.activate();
66
67 rawMasters = theInstance.getMap("masters");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070068 rawTerms = theInstance.getMap("terms");
69 backups = theInstance.getMap("backups");
tomb41d1ac2014-09-24 01:51:24 -070070
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070071 //TODO: hook up maps to event notification
72 //OptionalCacheLoader<DeviceId, NodeId> nodeLoader
73 //= new OptionalCacheLoader<>(kryoSerializationService, rawMasters);
74 //masters = new AbsentInvalidatingLoadingCache<>(newBuilder().build(nodeLoader));
75 //rawMasters.addEntryListener(new RemoteMasterShipEventHandler(masters), true);
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -070076
tomb41d1ac2014-09-24 01:51:24 -070077 log.info("Started");
78 }
79
80 @Deactivate
81 public void deactivate() {
82 log.info("Stopped");
83 }
84
85 @Override
Ayaka Koshibe406d0102014-09-24 16:08:12 -070086 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070087 byte [] did = serialize(deviceId);
88 byte [] nid = serialize(nodeId);
tomb41d1ac2014-09-24 01:51:24 -070089
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070090 ILock lock = theInstance.getLock(LOCK);
91 lock.lock();
92 try {
93 MastershipRole role = getRole(nodeId, deviceId);
94 Integer term = rawTerms.get(did);
95 switch (role) {
96 case MASTER:
97 return null;
98 case STANDBY:
99 rawMasters.put(did, nid);
100 rawTerms.put(did, ++term);
101 backups.putIfAbsent(nid, NIL);
102 break;
103 case NONE:
104 rawMasters.put(did, nid);
105 //new switch OR state transition after being orphaned
106 if (term == null) {
107 rawTerms.put(did, INIT);
108 } else {
109 rawTerms.put(did, ++term);
110 }
111 backups.put(nid, NIL);
112 break;
113 default:
114 log.warn("unknown Mastership Role {}", role);
115 return null;
116 }
117 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
118 } finally {
119 lock.unlock();
tomb41d1ac2014-09-24 01:51:24 -0700120 }
121 }
122
123 @Override
124 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700125 return deserialize(rawMasters.get(serialize(deviceId)));
tomb41d1ac2014-09-24 01:51:24 -0700126 }
127
128 @Override
129 public Set<DeviceId> getDevices(NodeId nodeId) {
130 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700131
132 for (Map.Entry<byte[], byte[]> entry : rawMasters.entrySet()) {
133 if (nodeId.equals(deserialize(entry.getValue()))) {
134 builder.add((DeviceId) deserialize(entry.getKey()));
tomb41d1ac2014-09-24 01:51:24 -0700135 }
136 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700137
tomb41d1ac2014-09-24 01:51:24 -0700138 return builder.build();
139 }
140
141 @Override
142 public MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700143 // first to empty slot for device in master map is MASTER
144 // depending on how backups are organized, might need to trigger election
145 // so only controller doesn't set itself to backup for another device
146 byte [] did = serialize(deviceId);
147 NodeId local = clusterService.getLocalNode().id();
148 byte [] lnid = serialize(local);
149
150 ILock lock = theInstance.getLock(LOCK);
151 lock.lock();
152 try {
153 MastershipRole role = getRole(local, deviceId);
154 switch (role) {
155 case MASTER:
156 break;
157 case STANDBY:
158 backups.put(lnid, NIL);
159 rawTerms.putIfAbsent(did, INIT);
160 break;
161 case NONE:
162 rawMasters.put(did, lnid);
163 rawTerms.putIfAbsent(did, INIT);
164 backups.put(lnid, NIL);
165 role = MastershipRole.MASTER;
166 break;
167 default:
168 log.warn("unknown Mastership Role {}", role);
169 }
170 return role;
171 } finally {
172 lock.unlock();
173 }
tomb41d1ac2014-09-24 01:51:24 -0700174 }
175
176 @Override
177 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700178 byte[] did = serialize(deviceId);
179
180 NodeId current = deserialize(rawMasters.get(did));
181 MastershipRole role = null;
182
183 if (current == null) {
184 //IFF no controllers have claimed mastership over it
185 role = MastershipRole.NONE;
186 } else {
187 if (current.equals(nodeId)) {
188 role = MastershipRole.MASTER;
189 } else {
190 role = MastershipRole.STANDBY;
191 }
192 }
193
194 return role;
tomb41d1ac2014-09-24 01:51:24 -0700195 }
196
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700197 @Override
198 public MastershipTerm getTermFor(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700199 byte[] did = serialize(deviceId);
200
201 if ((rawMasters.get(did) == null) ||
202 (rawTerms.get(did) == null)) {
203 return null;
204 }
205 return MastershipTerm.of(
206 (NodeId) deserialize(rawMasters.get(did)), rawTerms.get(did));
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700207 }
208
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700209 @Override
210 public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700211 byte [] did = serialize(deviceId);
212
213 ILock lock = theInstance.getLock(LOCK);
214 lock.lock();
215 try {
216 MastershipRole role = getRole(nodeId, deviceId);
217 switch (role) {
218 case MASTER:
219 //hand off device to another
220 NodeId backup = reelect(nodeId, deviceId);
221 if (backup == null) {
222 //goes back to NONE
223 rawMasters.remove(did);
224 } else {
225 //goes to STANDBY for local, MASTER for someone else
226 Integer term = rawTerms.get(did);
227 rawMasters.put(did, serialize(backup));
228 rawTerms.put(did, ++term);
229 return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
230 }
231 case STANDBY:
232 case NONE:
233 break;
234 default:
235 log.warn("unknown Mastership Role {}", role);
236 }
237 return null;
238 } finally {
239 lock.unlock();
240 }
241 }
242
243 //helper for "re-electing" a new master for a given device
244 private NodeId reelect(NodeId current, DeviceId deviceId) {
245 for (byte [] node : backups.keySet()) {
246 NodeId nid = deserialize(node);
247 if (!current.equals(nid)) {
248 return nid;
249 }
250 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700251 return null;
252 }
253
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700254 //adds node to pool(s) of backup
255 private void backup(NodeId nodeId, DeviceId deviceId) {
256 //TODO might be useful to isolate out
257 }
258
Yuta HIGUCHIfec9e192014-09-28 14:58:02 -0700259 private class RemoteMasterShipEventHandler extends RemoteCacheEventHandler<DeviceId, NodeId> {
alshabib339a3d92014-09-26 17:54:32 -0700260 public RemoteMasterShipEventHandler(LoadingCache<DeviceId, Optional<NodeId>> cache) {
261 super(cache);
262 }
263
264 @Override
265 protected void onAdd(DeviceId deviceId, NodeId nodeId) {
266 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
267 }
268
269 @Override
270 protected void onRemove(DeviceId deviceId, NodeId nodeId) {
271 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
272 }
273
274 @Override
275 protected void onUpdate(DeviceId deviceId, NodeId oldNodeId, NodeId nodeId) {
276 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
277 }
278 }
279
tomb41d1ac2014-09-24 01:51:24 -0700280}