blob: df810dc73a2a9a0daff2ba06b6c0ec43778512bc [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.trivial.impl;
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;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070030import java.util.concurrent.atomic.AtomicInteger;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070031
32import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080035import org.apache.felix.scr.annotations.Reference;
36import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070037import org.apache.felix.scr.annotations.Service;
Madan Jampani7d2fab22015-03-18 17:21:57 -070038import org.joda.time.DateTime;
39import org.onlab.packet.IpAddress;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.cluster.ClusterEventListener;
41import org.onosproject.cluster.ClusterService;
42import org.onosproject.cluster.ControllerNode;
43import org.onosproject.cluster.ControllerNode.State;
44import org.onosproject.cluster.DefaultControllerNode;
45import org.onosproject.cluster.NodeId;
46import org.onosproject.cluster.RoleInfo;
47import org.onosproject.mastership.MastershipEvent;
48import org.onosproject.mastership.MastershipStore;
49import org.onosproject.mastership.MastershipStoreDelegate;
50import org.onosproject.mastership.MastershipTerm;
51import org.onosproject.net.DeviceId;
52import org.onosproject.net.MastershipRole;
53import org.onosproject.store.AbstractStore;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070054import org.slf4j.Logger;
55
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080056import com.google.common.collect.ImmutableList;
57import com.google.common.collect.ImmutableSet;
Ayaka Koshibefc981cf2014-10-21 12:44:17 -070058
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070059/**
60 * Manages inventory of controller mastership over devices using
Ayaka Koshibe406d0102014-09-24 16:08:12 -070061 * trivial, non-distributed in-memory structures implementation.
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070062 */
63@Component(immediate = true)
64@Service
tomf80c9722014-09-24 14:49:18 -070065public class SimpleMastershipStore
66 extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
67 implements MastershipStore {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070068
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070069 private final Logger log = getLogger(getClass());
70
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -070071 private static final int NOTHING = 0;
72 private static final int INIT = 1;
73
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080074 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected ClusterService clusterService;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070076
77 //devices mapped to their masters, to emulate multiple nodes
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070078 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070079 //emulate backups with pile of nodes
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080080 protected final Map<DeviceId, List<NodeId>> backups = new HashMap<>();
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070081 //terms
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070082 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070083
84 @Activate
85 public void activate() {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080086 if (clusterService == null) {
87 // just for ease of unit test
88 final ControllerNode instance =
89 new DefaultControllerNode(new NodeId("local"),
90 IpAddress.valueOf("127.0.0.1"));
91
92 clusterService = new ClusterService() {
93
Madan Jampani7d2fab22015-03-18 17:21:57 -070094 private final DateTime creationTime = DateTime.now();
95
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080096 @Override
97 public ControllerNode getLocalNode() {
98 return instance;
99 }
100
101 @Override
102 public Set<ControllerNode> getNodes() {
103 return ImmutableSet.of(instance);
104 }
105
106 @Override
107 public ControllerNode getNode(NodeId nodeId) {
108 if (instance.id().equals(nodeId)) {
109 return instance;
110 }
111 return null;
112 }
113
114 @Override
115 public State getState(NodeId nodeId) {
116 if (instance.id().equals(nodeId)) {
117 return State.ACTIVE;
118 } else {
119 return State.INACTIVE;
120 }
121 }
122
123 @Override
Madan Jampani7d2fab22015-03-18 17:21:57 -0700124 public DateTime getLastUpdated(NodeId nodeId) {
125 return creationTime;
126 }
127
128 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800129 public void addListener(ClusterEventListener listener) {
130 }
131
132 @Override
133 public void removeListener(ClusterEventListener listener) {
134 }
135 };
136 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700137 log.info("Started");
138 }
139
140 @Deactivate
141 public void deactivate() {
142 log.info("Stopped");
143 }
144
145 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800146 public synchronized MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700147
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800148 MastershipRole role = getRole(nodeId, deviceId);
149 switch (role) {
150 case MASTER:
151 // no-op
152 return null;
153 case STANDBY:
154 case NONE:
155 NodeId prevMaster = masterMap.put(deviceId, nodeId);
156 incrementTerm(deviceId);
157 removeFromBackups(deviceId, nodeId);
158 addToBackup(deviceId, prevMaster);
159 break;
160 default:
161 log.warn("unknown Mastership Role {}", role);
162 return null;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700163 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700164
Ayaka Koshibefc981cf2014-10-21 12:44:17 -0700165 return new MastershipEvent(MASTER_CHANGED, deviceId,
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800166 getNodes(deviceId));
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700167 }
168
169 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700170 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700171 return masterMap.get(deviceId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700172 }
173
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800174 // synchronized for atomic read
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700175 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800176 public synchronized RoleInfo getNodes(DeviceId deviceId) {
177 return new RoleInfo(masterMap.get(deviceId),
178 backups.getOrDefault(deviceId, ImmutableList.of()));
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700179 }
180
181 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700182 public Set<DeviceId> getDevices(NodeId nodeId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700183 Set<DeviceId> ids = new HashSet<>();
184 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
Thomas Vachuskaa68be812014-11-27 11:49:54 -0800185 if (Objects.equals(d.getValue(), nodeId)) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700186 ids.add(d.getKey());
187 }
188 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800189 return ids;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700190 }
191
192 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800193 public synchronized MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700194 //query+possible reelection
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800195 NodeId node = clusterService.getLocalNode().id();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700196 MastershipRole role = getRole(node, deviceId);
197
198 switch (role) {
199 case MASTER:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800200 return MastershipRole.MASTER;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700201 case STANDBY:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800202 if (getMaster(deviceId) == null) {
203 // no master => become master
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700204 masterMap.put(deviceId, node);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800205 incrementTerm(deviceId);
206 // remove from backup list
207 removeFromBackups(deviceId, node);
208 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
209 getNodes(deviceId)));
210 return MastershipRole.MASTER;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700211 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800212 return MastershipRole.STANDBY;
213 case NONE:
214 if (getMaster(deviceId) == null) {
215 // no master => become master
216 masterMap.put(deviceId, node);
217 incrementTerm(deviceId);
218 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
219 getNodes(deviceId)));
220 return MastershipRole.MASTER;
221 }
222 // add to backup list
223 if (addToBackup(deviceId, node)) {
224 notifyDelegate(new MastershipEvent(BACKUPS_CHANGED, deviceId,
225 getNodes(deviceId)));
226 }
227 return MastershipRole.STANDBY;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700228 default:
229 log.warn("unknown Mastership Role {}", role);
230 }
231 return role;
tomb41d1ac2014-09-24 01:51:24 -0700232 }
233
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800234 // add to backup if not there already, silently ignores null node
235 private synchronized boolean addToBackup(DeviceId deviceId, NodeId nodeId) {
236 boolean modified = false;
237 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
238 if (nodeId != null && !stbys.contains(nodeId)) {
239 stbys.add(nodeId);
240 modified = true;
241 }
242 backups.put(deviceId, stbys);
243 return modified;
244 }
245
246 private synchronized boolean removeFromBackups(DeviceId deviceId, NodeId node) {
247 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
248 boolean modified = stbys.remove(node);
249 backups.put(deviceId, stbys);
250 return modified;
251 }
252
253 private synchronized void incrementTerm(DeviceId deviceId) {
254 AtomicInteger term = termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING));
255 term.incrementAndGet();
256 termMap.put(deviceId, term);
257 }
258
tomb41d1ac2014-09-24 01:51:24 -0700259 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700260 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700261 //just query
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700262 NodeId current = masterMap.get(deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700263 MastershipRole role;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700264
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800265 if (current != null && current.equals(nodeId)) {
266 return MastershipRole.MASTER;
267 }
268
269 if (backups.getOrDefault(deviceId, Collections.emptyList()).contains(nodeId)) {
270 role = MastershipRole.STANDBY;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700271 } else {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800272 role = MastershipRole.NONE;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700273 }
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700274 return role;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700275 }
276
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800277 // synchronized for atomic read
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700278 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800279 public synchronized MastershipTerm getTermFor(DeviceId deviceId) {
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700280 if ((termMap.get(deviceId) == null)) {
281 return MastershipTerm.of(masterMap.get(deviceId), NOTHING);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700282 }
283 return MastershipTerm.of(
284 masterMap.get(deviceId), termMap.get(deviceId).get());
285 }
286
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700287 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800288 public synchronized MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700289 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800290 switch (role) {
291 case MASTER:
292 NodeId backup = reelect(deviceId, nodeId);
293 if (backup == null) {
294 // no master alternative
295 masterMap.remove(deviceId);
296 // TODO: Should there be new event type for no MASTER?
297 return new MastershipEvent(MASTER_CHANGED, deviceId,
298 getNodes(deviceId));
299 } else {
300 NodeId prevMaster = masterMap.put(deviceId, backup);
301 incrementTerm(deviceId);
302 addToBackup(deviceId, prevMaster);
303 return new MastershipEvent(MASTER_CHANGED, deviceId,
304 getNodes(deviceId));
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700305 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800306
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800307 case STANDBY:
308 case NONE:
309 boolean modified = addToBackup(deviceId, nodeId);
310 if (modified) {
311 return new MastershipEvent(BACKUPS_CHANGED, deviceId,
312 getNodes(deviceId));
313 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800314 break;
315
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800316 default:
317 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700318 }
319 return null;
320 }
321
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700322 //dumbly selects next-available node that's not the current one
323 //emulate leader election
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800324 private synchronized NodeId reelect(DeviceId did, NodeId nodeId) {
325 List<NodeId> stbys = backups.getOrDefault(did, Collections.emptyList());
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700326 NodeId backup = null;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800327 for (NodeId n : stbys) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700328 if (!n.equals(nodeId)) {
329 backup = n;
330 break;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700331 }
332 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800333 stbys.remove(backup);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700334 return backup;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700335 }
336
Ayaka Koshibec4047702014-10-07 14:43:52 -0700337 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800338 public synchronized MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700339 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800340 switch (role) {
341 case MASTER:
342 NodeId backup = reelect(deviceId, nodeId);
343 masterMap.put(deviceId, backup);
344 incrementTerm(deviceId);
345 return new MastershipEvent(MASTER_CHANGED, deviceId,
346 getNodes(deviceId));
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800347
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800348 case STANDBY:
349 if (removeFromBackups(deviceId, nodeId)) {
350 return new MastershipEvent(BACKUPS_CHANGED, deviceId,
351 getNodes(deviceId));
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700352 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800353 break;
354
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800355 case NONE:
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800356 break;
357
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800358 default:
359 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700360 }
361 return null;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700362 }
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800363
364 @Override
365 public synchronized void relinquishAllRole(NodeId nodeId) {
366 List<MastershipEvent> events = new ArrayList<>();
367 Set<DeviceId> toRelinquish = new HashSet<>();
368
369 masterMap.entrySet().stream()
370 .filter(entry -> nodeId.equals(entry.getValue()))
371 .forEach(entry -> toRelinquish.add(entry.getKey()));
372
373 backups.entrySet().stream()
374 .filter(entry -> entry.getValue().contains(nodeId))
375 .forEach(entry -> toRelinquish.add(entry.getKey()));
376
377 toRelinquish.forEach(deviceId -> {
378 MastershipEvent event = relinquishRole(nodeId, deviceId);
379 if (event != null) {
380 events.add(event);
381 }
382 });
383
384 notifyDelegate(events);
385 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700386}