blob: ef92ded2fae6407ceed465ffbc758cd17df2572c [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Thomas Vachuskac97aa612015-06-23 16:00:18 -070016package org.onosproject.store.trivial;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070017
Madan Jampani7d2fab22015-03-18 17:21:57 -070018import static org.onosproject.mastership.MastershipEvent.Type.BACKUPS_CHANGED;
19import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070020import static org.slf4j.LoggerFactory.getLogger;
21
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070022import java.util.ArrayList;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070023import java.util.Collections;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070024import java.util.HashMap;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070025import java.util.HashSet;
Ayaka Koshibe45503ce2014-10-14 11:26:45 -070026import java.util.List;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070027import java.util.Map;
Thomas Vachuskaa68be812014-11-27 11:49:54 -080028import java.util.Objects;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070029import java.util.Set;
Madan Jampanif7536ab2015-05-07 23:23:23 -070030import java.util.concurrent.CompletableFuture;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070031import java.util.concurrent.atomic.AtomicInteger;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070032
33import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080036import org.apache.felix.scr.annotations.Reference;
37import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070038import org.apache.felix.scr.annotations.Service;
Madan Jampani7d2fab22015-03-18 17:21:57 -070039import org.joda.time.DateTime;
40import org.onlab.packet.IpAddress;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.cluster.ClusterEventListener;
42import org.onosproject.cluster.ClusterService;
43import org.onosproject.cluster.ControllerNode;
44import org.onosproject.cluster.ControllerNode.State;
45import org.onosproject.cluster.DefaultControllerNode;
46import org.onosproject.cluster.NodeId;
47import org.onosproject.cluster.RoleInfo;
48import org.onosproject.mastership.MastershipEvent;
49import org.onosproject.mastership.MastershipStore;
50import org.onosproject.mastership.MastershipStoreDelegate;
51import org.onosproject.mastership.MastershipTerm;
52import org.onosproject.net.DeviceId;
53import org.onosproject.net.MastershipRole;
54import org.onosproject.store.AbstractStore;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070055import org.slf4j.Logger;
56
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080057import com.google.common.collect.ImmutableList;
58import com.google.common.collect.ImmutableSet;
Ayaka Koshibefc981cf2014-10-21 12:44:17 -070059
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070060/**
61 * Manages inventory of controller mastership over devices using
Ayaka Koshibe406d0102014-09-24 16:08:12 -070062 * trivial, non-distributed in-memory structures implementation.
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070063 */
64@Component(immediate = true)
65@Service
tomf80c9722014-09-24 14:49:18 -070066public class SimpleMastershipStore
67 extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
68 implements MastershipStore {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070069
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070070 private final Logger log = getLogger(getClass());
71
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -070072 private static final int NOTHING = 0;
73 private static final int INIT = 1;
74
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080075 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected ClusterService clusterService;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070077
78 //devices mapped to their masters, to emulate multiple nodes
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070079 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070080 //emulate backups with pile of nodes
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080081 protected final Map<DeviceId, List<NodeId>> backups = new HashMap<>();
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070082 //terms
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070083 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070084
85 @Activate
86 public void activate() {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080087 if (clusterService == null) {
88 // just for ease of unit test
89 final ControllerNode instance =
90 new DefaultControllerNode(new NodeId("local"),
91 IpAddress.valueOf("127.0.0.1"));
92
93 clusterService = new ClusterService() {
94
Madan Jampani7d2fab22015-03-18 17:21:57 -070095 private final DateTime creationTime = DateTime.now();
96
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080097 @Override
98 public ControllerNode getLocalNode() {
99 return instance;
100 }
101
102 @Override
103 public Set<ControllerNode> getNodes() {
104 return ImmutableSet.of(instance);
105 }
106
107 @Override
108 public ControllerNode getNode(NodeId nodeId) {
109 if (instance.id().equals(nodeId)) {
110 return instance;
111 }
112 return null;
113 }
114
115 @Override
116 public State getState(NodeId nodeId) {
117 if (instance.id().equals(nodeId)) {
118 return State.ACTIVE;
119 } else {
120 return State.INACTIVE;
121 }
122 }
123
124 @Override
Madan Jampani7d2fab22015-03-18 17:21:57 -0700125 public DateTime getLastUpdated(NodeId nodeId) {
126 return creationTime;
127 }
128
129 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800130 public void addListener(ClusterEventListener listener) {
131 }
132
133 @Override
134 public void removeListener(ClusterEventListener listener) {
135 }
136 };
137 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700138 log.info("Started");
139 }
140
141 @Deactivate
142 public void deactivate() {
143 log.info("Stopped");
144 }
145
146 @Override
Madan Jampanif7536ab2015-05-07 23:23:23 -0700147 public synchronized CompletableFuture<MastershipEvent> setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700148
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800149 MastershipRole role = getRole(nodeId, deviceId);
150 switch (role) {
151 case MASTER:
152 // no-op
Madan Jampanif7536ab2015-05-07 23:23:23 -0700153 return CompletableFuture.completedFuture(null);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800154 case STANDBY:
155 case NONE:
156 NodeId prevMaster = masterMap.put(deviceId, nodeId);
157 incrementTerm(deviceId);
158 removeFromBackups(deviceId, nodeId);
159 addToBackup(deviceId, prevMaster);
160 break;
161 default:
162 log.warn("unknown Mastership Role {}", role);
163 return null;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700164 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700165
Madan Jampanif7536ab2015-05-07 23:23:23 -0700166 return CompletableFuture.completedFuture(
167 new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700168 }
169
170 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700171 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700172 return masterMap.get(deviceId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700173 }
174
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800175 // synchronized for atomic read
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700176 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800177 public synchronized RoleInfo getNodes(DeviceId deviceId) {
178 return new RoleInfo(masterMap.get(deviceId),
179 backups.getOrDefault(deviceId, ImmutableList.of()));
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700180 }
181
182 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700183 public Set<DeviceId> getDevices(NodeId nodeId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700184 Set<DeviceId> ids = new HashSet<>();
185 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
Thomas Vachuskaa68be812014-11-27 11:49:54 -0800186 if (Objects.equals(d.getValue(), nodeId)) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700187 ids.add(d.getKey());
188 }
189 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800190 return ids;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700191 }
192
193 @Override
Madan Jampanide003d92015-05-11 17:14:20 -0700194 public synchronized CompletableFuture<MastershipRole> requestRole(DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700195 //query+possible reelection
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800196 NodeId node = clusterService.getLocalNode().id();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700197 MastershipRole role = getRole(node, deviceId);
198
199 switch (role) {
200 case MASTER:
Madan Jampanide003d92015-05-11 17:14:20 -0700201 return CompletableFuture.completedFuture(MastershipRole.MASTER);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700202 case STANDBY:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800203 if (getMaster(deviceId) == null) {
204 // no master => become master
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700205 masterMap.put(deviceId, node);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800206 incrementTerm(deviceId);
207 // remove from backup list
208 removeFromBackups(deviceId, node);
209 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
210 getNodes(deviceId)));
Madan Jampanide003d92015-05-11 17:14:20 -0700211 return CompletableFuture.completedFuture(MastershipRole.MASTER);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700212 }
Madan Jampanide003d92015-05-11 17:14:20 -0700213 return CompletableFuture.completedFuture(MastershipRole.STANDBY);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800214 case NONE:
215 if (getMaster(deviceId) == null) {
216 // no master => become master
217 masterMap.put(deviceId, node);
218 incrementTerm(deviceId);
219 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
220 getNodes(deviceId)));
Madan Jampanide003d92015-05-11 17:14:20 -0700221 return CompletableFuture.completedFuture(MastershipRole.MASTER);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800222 }
223 // add to backup list
224 if (addToBackup(deviceId, node)) {
225 notifyDelegate(new MastershipEvent(BACKUPS_CHANGED, deviceId,
226 getNodes(deviceId)));
227 }
Madan Jampanide003d92015-05-11 17:14:20 -0700228 return CompletableFuture.completedFuture(MastershipRole.STANDBY);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700229 default:
230 log.warn("unknown Mastership Role {}", role);
231 }
Madan Jampanide003d92015-05-11 17:14:20 -0700232 return CompletableFuture.completedFuture(role);
tomb41d1ac2014-09-24 01:51:24 -0700233 }
234
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800235 // add to backup if not there already, silently ignores null node
236 private synchronized boolean addToBackup(DeviceId deviceId, NodeId nodeId) {
237 boolean modified = false;
238 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
239 if (nodeId != null && !stbys.contains(nodeId)) {
240 stbys.add(nodeId);
241 modified = true;
242 }
243 backups.put(deviceId, stbys);
244 return modified;
245 }
246
247 private synchronized boolean removeFromBackups(DeviceId deviceId, NodeId node) {
248 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
249 boolean modified = stbys.remove(node);
250 backups.put(deviceId, stbys);
251 return modified;
252 }
253
254 private synchronized void incrementTerm(DeviceId deviceId) {
255 AtomicInteger term = termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING));
256 term.incrementAndGet();
257 termMap.put(deviceId, term);
258 }
259
tomb41d1ac2014-09-24 01:51:24 -0700260 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700261 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700262 //just query
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700263 NodeId current = masterMap.get(deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700264 MastershipRole role;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700265
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800266 if (current != null && current.equals(nodeId)) {
267 return MastershipRole.MASTER;
268 }
269
270 if (backups.getOrDefault(deviceId, Collections.emptyList()).contains(nodeId)) {
271 role = MastershipRole.STANDBY;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700272 } else {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800273 role = MastershipRole.NONE;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700274 }
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700275 return role;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700276 }
277
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800278 // synchronized for atomic read
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700279 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800280 public synchronized MastershipTerm getTermFor(DeviceId deviceId) {
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700281 if ((termMap.get(deviceId) == null)) {
282 return MastershipTerm.of(masterMap.get(deviceId), NOTHING);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700283 }
284 return MastershipTerm.of(
285 masterMap.get(deviceId), termMap.get(deviceId).get());
286 }
287
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700288 @Override
Madan Jampanif7536ab2015-05-07 23:23:23 -0700289 public synchronized CompletableFuture<MastershipEvent> setStandby(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700290 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800291 switch (role) {
292 case MASTER:
293 NodeId backup = reelect(deviceId, nodeId);
294 if (backup == null) {
295 // no master alternative
296 masterMap.remove(deviceId);
297 // TODO: Should there be new event type for no MASTER?
Madan Jampanif7536ab2015-05-07 23:23:23 -0700298 return CompletableFuture.completedFuture(
299 new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800300 } else {
301 NodeId prevMaster = masterMap.put(deviceId, backup);
302 incrementTerm(deviceId);
303 addToBackup(deviceId, prevMaster);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700304 return CompletableFuture.completedFuture(
305 new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700306 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800307
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800308 case STANDBY:
309 case NONE:
310 boolean modified = addToBackup(deviceId, nodeId);
311 if (modified) {
Madan Jampanif7536ab2015-05-07 23:23:23 -0700312 return CompletableFuture.completedFuture(
313 new MastershipEvent(BACKUPS_CHANGED, deviceId, getNodes(deviceId)));
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800314 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800315 break;
316
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800317 default:
318 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700319 }
320 return null;
321 }
322
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700323 //dumbly selects next-available node that's not the current one
324 //emulate leader election
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800325 private synchronized NodeId reelect(DeviceId did, NodeId nodeId) {
326 List<NodeId> stbys = backups.getOrDefault(did, Collections.emptyList());
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700327 NodeId backup = null;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800328 for (NodeId n : stbys) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700329 if (!n.equals(nodeId)) {
330 backup = n;
331 break;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700332 }
333 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800334 stbys.remove(backup);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700335 return backup;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700336 }
337
Ayaka Koshibec4047702014-10-07 14:43:52 -0700338 @Override
Madan Jampanif7536ab2015-05-07 23:23:23 -0700339 public synchronized CompletableFuture<MastershipEvent> relinquishRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700340 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800341 switch (role) {
342 case MASTER:
343 NodeId backup = reelect(deviceId, nodeId);
344 masterMap.put(deviceId, backup);
345 incrementTerm(deviceId);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700346 return CompletableFuture.completedFuture(
347 new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800348
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800349 case STANDBY:
350 if (removeFromBackups(deviceId, nodeId)) {
Madan Jampanif7536ab2015-05-07 23:23:23 -0700351 return CompletableFuture.completedFuture(
352 new MastershipEvent(BACKUPS_CHANGED, deviceId, getNodes(deviceId)));
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700353 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800354 break;
355
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800356 case NONE:
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800357 break;
358
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800359 default:
360 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700361 }
Madan Jampanif7536ab2015-05-07 23:23:23 -0700362 return CompletableFuture.completedFuture(null);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700363 }
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800364
365 @Override
366 public synchronized void relinquishAllRole(NodeId nodeId) {
Madan Jampanif7536ab2015-05-07 23:23:23 -0700367 List<CompletableFuture<MastershipEvent>> eventFutures = new ArrayList<>();
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800368 Set<DeviceId> toRelinquish = new HashSet<>();
369
370 masterMap.entrySet().stream()
371 .filter(entry -> nodeId.equals(entry.getValue()))
372 .forEach(entry -> toRelinquish.add(entry.getKey()));
373
374 backups.entrySet().stream()
375 .filter(entry -> entry.getValue().contains(nodeId))
376 .forEach(entry -> toRelinquish.add(entry.getKey()));
377
378 toRelinquish.forEach(deviceId -> {
Madan Jampanif7536ab2015-05-07 23:23:23 -0700379 eventFutures.add(relinquishRole(nodeId, deviceId));
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800380 });
381
Madan Jampanif7536ab2015-05-07 23:23:23 -0700382 eventFutures.forEach(future -> {
383 future.whenComplete((event, error) -> {
384 notifyDelegate(event);
385 });
386 });
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800387 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700388}