blob: 6a96c01daf7a7926e9febbe1aa2e0b30eb3a369d [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;
13import org.apache.felix.scr.annotations.Service;
14import org.onlab.onos.cluster.ClusterService;
15import org.onlab.onos.cluster.MastershipEvent;
16import org.onlab.onos.cluster.MastershipStore;
tom0755a362014-09-24 11:54:43 -070017import org.onlab.onos.cluster.MastershipStoreDelegate;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070018import org.onlab.onos.cluster.MastershipTerm;
tomb41d1ac2014-09-24 01:51:24 -070019import org.onlab.onos.cluster.NodeId;
20import org.onlab.onos.net.DeviceId;
21import org.onlab.onos.net.MastershipRole;
Yuta HIGUCHIb5df76d2014-09-27 20:54:00 -070022import org.onlab.onos.store.common.AbstractHazelcastStore;
tomb41d1ac2014-09-24 01:51:24 -070023
alshabib339a3d92014-09-26 17:54:32 -070024import com.google.common.collect.ImmutableSet;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070025import com.hazelcast.core.ILock;
alshabib339a3d92014-09-26 17:54:32 -070026import com.hazelcast.core.IMap;
tomb41d1ac2014-09-24 01:51:24 -070027
28/**
29 * Distributed implementation of the cluster nodes store.
30 */
31@Component(immediate = true)
32@Service
tom0755a362014-09-24 11:54:43 -070033public class DistributedMastershipStore
Yuta HIGUCHI2e963892014-09-27 13:00:39 -070034extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
alshabib339a3d92014-09-26 17:54:32 -070035implements MastershipStore {
tomb41d1ac2014-09-24 01:51:24 -070036
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070037 //arbitrary lock name
38 private static final String LOCK = "lock";
39 //initial term value
40 private static final Integer INIT = 0;
41 //placeholder non-null value
42 private static final Byte NIL = 0x0;
43
44 //devices to masters
45 protected IMap<byte[], byte[]> rawMasters;
46 //devices to terms
47 protected IMap<byte[], Integer> rawTerms;
48 //collection of nodes. values are ignored, as it's used as a makeshift 'set'
49 protected IMap<byte[], Byte> backups;
50
tomb41d1ac2014-09-24 01:51:24 -070051 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
52 protected ClusterService clusterService;
53
Ayaka Koshibe406d0102014-09-24 16:08:12 -070054 @Override
tomb41d1ac2014-09-24 01:51:24 -070055 @Activate
56 public void activate() {
57 super.activate();
58
59 rawMasters = theInstance.getMap("masters");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070060 rawTerms = theInstance.getMap("terms");
61 backups = theInstance.getMap("backups");
tomb41d1ac2014-09-24 01:51:24 -070062
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070063 rawMasters.addEntryListener(new RemoteMasterShipEventHandler(), true);
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -070064
tomb41d1ac2014-09-24 01:51:24 -070065 log.info("Started");
66 }
67
68 @Deactivate
69 public void deactivate() {
70 log.info("Stopped");
71 }
72
73 @Override
Ayaka Koshibe406d0102014-09-24 16:08:12 -070074 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070075 byte [] did = serialize(deviceId);
76 byte [] nid = serialize(nodeId);
tomb41d1ac2014-09-24 01:51:24 -070077
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070078 ILock lock = theInstance.getLock(LOCK);
79 lock.lock();
80 try {
81 MastershipRole role = getRole(nodeId, deviceId);
82 Integer term = rawTerms.get(did);
83 switch (role) {
84 case MASTER:
85 return null;
86 case STANDBY:
87 rawMasters.put(did, nid);
88 rawTerms.put(did, ++term);
89 backups.putIfAbsent(nid, NIL);
90 break;
91 case NONE:
92 rawMasters.put(did, nid);
93 //new switch OR state transition after being orphaned
94 if (term == null) {
95 rawTerms.put(did, INIT);
96 } else {
97 rawTerms.put(did, ++term);
98 }
99 backups.put(nid, NIL);
100 break;
101 default:
102 log.warn("unknown Mastership Role {}", role);
103 return null;
104 }
105 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
106 } finally {
107 lock.unlock();
tomb41d1ac2014-09-24 01:51:24 -0700108 }
109 }
110
111 @Override
112 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700113 return deserialize(rawMasters.get(serialize(deviceId)));
tomb41d1ac2014-09-24 01:51:24 -0700114 }
115
116 @Override
117 public Set<DeviceId> getDevices(NodeId nodeId) {
118 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700119
120 for (Map.Entry<byte[], byte[]> entry : rawMasters.entrySet()) {
121 if (nodeId.equals(deserialize(entry.getValue()))) {
122 builder.add((DeviceId) deserialize(entry.getKey()));
tomb41d1ac2014-09-24 01:51:24 -0700123 }
124 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700125
tomb41d1ac2014-09-24 01:51:24 -0700126 return builder.build();
127 }
128
129 @Override
130 public MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700131 // first to empty slot for device in master map is MASTER
132 // depending on how backups are organized, might need to trigger election
133 // so only controller doesn't set itself to backup for another device
134 byte [] did = serialize(deviceId);
135 NodeId local = clusterService.getLocalNode().id();
136 byte [] lnid = serialize(local);
137
138 ILock lock = theInstance.getLock(LOCK);
139 lock.lock();
140 try {
141 MastershipRole role = getRole(local, deviceId);
142 switch (role) {
143 case MASTER:
144 break;
145 case STANDBY:
146 backups.put(lnid, NIL);
147 rawTerms.putIfAbsent(did, INIT);
148 break;
149 case NONE:
150 rawMasters.put(did, lnid);
151 rawTerms.putIfAbsent(did, INIT);
152 backups.put(lnid, NIL);
153 role = MastershipRole.MASTER;
154 break;
155 default:
156 log.warn("unknown Mastership Role {}", role);
157 }
158 return role;
159 } finally {
160 lock.unlock();
161 }
tomb41d1ac2014-09-24 01:51:24 -0700162 }
163
164 @Override
165 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700166 byte[] did = serialize(deviceId);
167
168 NodeId current = deserialize(rawMasters.get(did));
169 MastershipRole role = null;
170
171 if (current == null) {
172 //IFF no controllers have claimed mastership over it
173 role = MastershipRole.NONE;
174 } else {
175 if (current.equals(nodeId)) {
176 role = MastershipRole.MASTER;
177 } else {
178 role = MastershipRole.STANDBY;
179 }
180 }
181
182 return role;
tomb41d1ac2014-09-24 01:51:24 -0700183 }
184
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700185 @Override
186 public MastershipTerm getTermFor(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700187 byte[] did = serialize(deviceId);
188
189 if ((rawMasters.get(did) == null) ||
190 (rawTerms.get(did) == null)) {
191 return null;
192 }
193 return MastershipTerm.of(
194 (NodeId) deserialize(rawMasters.get(did)), rawTerms.get(did));
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700195 }
196
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700197 @Override
198 public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700199 byte [] did = serialize(deviceId);
200
201 ILock lock = theInstance.getLock(LOCK);
202 lock.lock();
203 try {
204 MastershipRole role = getRole(nodeId, deviceId);
205 switch (role) {
206 case MASTER:
207 //hand off device to another
208 NodeId backup = reelect(nodeId, deviceId);
209 if (backup == null) {
210 //goes back to NONE
211 rawMasters.remove(did);
212 } else {
213 //goes to STANDBY for local, MASTER for someone else
214 Integer term = rawTerms.get(did);
215 rawMasters.put(did, serialize(backup));
216 rawTerms.put(did, ++term);
217 return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
218 }
219 case STANDBY:
220 case NONE:
221 break;
222 default:
223 log.warn("unknown Mastership Role {}", role);
224 }
225 return null;
226 } finally {
227 lock.unlock();
228 }
229 }
230
231 //helper for "re-electing" a new master for a given device
232 private NodeId reelect(NodeId current, DeviceId deviceId) {
233 for (byte [] node : backups.keySet()) {
234 NodeId nid = deserialize(node);
235 if (!current.equals(nid)) {
236 return nid;
237 }
238 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700239 return null;
240 }
241
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700242 //adds node to pool(s) of backup
243 private void backup(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700244 //TODO might be useful to isolate out this function and reelect() if we
245 //get more backup/election schemes
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700246 }
247
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700248 private class RemoteMasterShipEventHandler extends RemoteEventHandler<DeviceId, NodeId> {
alshabib339a3d92014-09-26 17:54:32 -0700249
250 @Override
251 protected void onAdd(DeviceId deviceId, NodeId nodeId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700252 //only addition indicates a change in mastership
alshabib339a3d92014-09-26 17:54:32 -0700253 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
254 }
255
256 @Override
257 protected void onRemove(DeviceId deviceId, NodeId nodeId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700258 //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
alshabib339a3d92014-09-26 17:54:32 -0700259 }
260
261 @Override
262 protected void onUpdate(DeviceId deviceId, NodeId oldNodeId, NodeId nodeId) {
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700263 //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
alshabib339a3d92014-09-26 17:54:32 -0700264 }
265 }
266
tomb41d1ac2014-09-24 01:51:24 -0700267}