blob: f9e42737bf05512b02389d807ffb2ef6cea72bea [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
18import static org.slf4j.LoggerFactory.getLogger;
19
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070020import java.util.ArrayList;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070021import java.util.Collections;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070022import java.util.HashMap;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070023import java.util.HashSet;
Ayaka Koshibe45503ce2014-10-14 11:26:45 -070024import java.util.List;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070025import java.util.Map;
Thomas Vachuskaa68be812014-11-27 11:49:54 -080026import java.util.Objects;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070027import java.util.Set;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070028import java.util.concurrent.atomic.AtomicInteger;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070029
30import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080033import org.apache.felix.scr.annotations.Reference;
34import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070035import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.cluster.ClusterEventListener;
37import org.onosproject.cluster.ClusterService;
38import org.onosproject.cluster.ControllerNode;
39import org.onosproject.cluster.ControllerNode.State;
40import org.onosproject.cluster.DefaultControllerNode;
41import org.onosproject.cluster.NodeId;
42import org.onosproject.cluster.RoleInfo;
43import org.onosproject.mastership.MastershipEvent;
44import org.onosproject.mastership.MastershipStore;
45import org.onosproject.mastership.MastershipStoreDelegate;
46import org.onosproject.mastership.MastershipTerm;
47import org.onosproject.net.DeviceId;
48import org.onosproject.net.MastershipRole;
49import org.onosproject.store.AbstractStore;
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070050import org.onlab.packet.IpAddress;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070051import org.slf4j.Logger;
52
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080053import com.google.common.collect.ImmutableList;
54import com.google.common.collect.ImmutableSet;
Ayaka Koshibefc981cf2014-10-21 12:44:17 -070055
Brian O'Connorabafb502014-12-02 22:26:20 -080056import static org.onosproject.mastership.MastershipEvent.Type.*;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070057
58/**
59 * Manages inventory of controller mastership over devices using
Ayaka Koshibe406d0102014-09-24 16:08:12 -070060 * trivial, non-distributed in-memory structures implementation.
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070061 */
62@Component(immediate = true)
63@Service
tomf80c9722014-09-24 14:49:18 -070064public class SimpleMastershipStore
65 extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
66 implements MastershipStore {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070067
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070068 private final Logger log = getLogger(getClass());
69
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -070070 private static final int NOTHING = 0;
71 private static final int INIT = 1;
72
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080073 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected ClusterService clusterService;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070075
76 //devices mapped to their masters, to emulate multiple nodes
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070077 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070078 //emulate backups with pile of nodes
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080079 protected final Map<DeviceId, List<NodeId>> backups = new HashMap<>();
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070080 //terms
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070081 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070082
83 @Activate
84 public void activate() {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080085 if (clusterService == null) {
86 // just for ease of unit test
87 final ControllerNode instance =
88 new DefaultControllerNode(new NodeId("local"),
89 IpAddress.valueOf("127.0.0.1"));
90
91 clusterService = new ClusterService() {
92
93 @Override
94 public ControllerNode getLocalNode() {
95 return instance;
96 }
97
98 @Override
99 public Set<ControllerNode> getNodes() {
100 return ImmutableSet.of(instance);
101 }
102
103 @Override
104 public ControllerNode getNode(NodeId nodeId) {
105 if (instance.id().equals(nodeId)) {
106 return instance;
107 }
108 return null;
109 }
110
111 @Override
112 public State getState(NodeId nodeId) {
113 if (instance.id().equals(nodeId)) {
114 return State.ACTIVE;
115 } else {
116 return State.INACTIVE;
117 }
118 }
119
120 @Override
121 public void addListener(ClusterEventListener listener) {
122 }
123
124 @Override
125 public void removeListener(ClusterEventListener listener) {
126 }
127 };
128 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700129 log.info("Started");
130 }
131
132 @Deactivate
133 public void deactivate() {
134 log.info("Stopped");
135 }
136
137 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800138 public synchronized MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700139
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800140 MastershipRole role = getRole(nodeId, deviceId);
141 switch (role) {
142 case MASTER:
143 // no-op
144 return null;
145 case STANDBY:
146 case NONE:
147 NodeId prevMaster = masterMap.put(deviceId, nodeId);
148 incrementTerm(deviceId);
149 removeFromBackups(deviceId, nodeId);
150 addToBackup(deviceId, prevMaster);
151 break;
152 default:
153 log.warn("unknown Mastership Role {}", role);
154 return null;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700155 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700156
Ayaka Koshibefc981cf2014-10-21 12:44:17 -0700157 return new MastershipEvent(MASTER_CHANGED, deviceId,
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800158 getNodes(deviceId));
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700159 }
160
161 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700162 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700163 return masterMap.get(deviceId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700164 }
165
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800166 // synchronized for atomic read
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700167 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800168 public synchronized RoleInfo getNodes(DeviceId deviceId) {
169 return new RoleInfo(masterMap.get(deviceId),
170 backups.getOrDefault(deviceId, ImmutableList.of()));
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700171 }
172
173 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700174 public Set<DeviceId> getDevices(NodeId nodeId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700175 Set<DeviceId> ids = new HashSet<>();
176 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
Thomas Vachuskaa68be812014-11-27 11:49:54 -0800177 if (Objects.equals(d.getValue(), nodeId)) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700178 ids.add(d.getKey());
179 }
180 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800181 return ids;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700182 }
183
184 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800185 public synchronized MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700186 //query+possible reelection
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800187 NodeId node = clusterService.getLocalNode().id();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700188 MastershipRole role = getRole(node, deviceId);
189
190 switch (role) {
191 case MASTER:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800192 return MastershipRole.MASTER;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700193 case STANDBY:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800194 if (getMaster(deviceId) == null) {
195 // no master => become master
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700196 masterMap.put(deviceId, node);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800197 incrementTerm(deviceId);
198 // remove from backup list
199 removeFromBackups(deviceId, node);
200 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
201 getNodes(deviceId)));
202 return MastershipRole.MASTER;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700203 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800204 return MastershipRole.STANDBY;
205 case NONE:
206 if (getMaster(deviceId) == null) {
207 // no master => become master
208 masterMap.put(deviceId, node);
209 incrementTerm(deviceId);
210 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
211 getNodes(deviceId)));
212 return MastershipRole.MASTER;
213 }
214 // add to backup list
215 if (addToBackup(deviceId, node)) {
216 notifyDelegate(new MastershipEvent(BACKUPS_CHANGED, deviceId,
217 getNodes(deviceId)));
218 }
219 return MastershipRole.STANDBY;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700220 default:
221 log.warn("unknown Mastership Role {}", role);
222 }
223 return role;
tomb41d1ac2014-09-24 01:51:24 -0700224 }
225
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800226 // add to backup if not there already, silently ignores null node
227 private synchronized boolean addToBackup(DeviceId deviceId, NodeId nodeId) {
228 boolean modified = false;
229 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
230 if (nodeId != null && !stbys.contains(nodeId)) {
231 stbys.add(nodeId);
232 modified = true;
233 }
234 backups.put(deviceId, stbys);
235 return modified;
236 }
237
238 private synchronized boolean removeFromBackups(DeviceId deviceId, NodeId node) {
239 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
240 boolean modified = stbys.remove(node);
241 backups.put(deviceId, stbys);
242 return modified;
243 }
244
245 private synchronized void incrementTerm(DeviceId deviceId) {
246 AtomicInteger term = termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING));
247 term.incrementAndGet();
248 termMap.put(deviceId, term);
249 }
250
tomb41d1ac2014-09-24 01:51:24 -0700251 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700252 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700253 //just query
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700254 NodeId current = masterMap.get(deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700255 MastershipRole role;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700256
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800257 if (current != null && current.equals(nodeId)) {
258 return MastershipRole.MASTER;
259 }
260
261 if (backups.getOrDefault(deviceId, Collections.emptyList()).contains(nodeId)) {
262 role = MastershipRole.STANDBY;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700263 } else {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800264 role = MastershipRole.NONE;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700265 }
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700266 return role;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700267 }
268
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800269 // synchronized for atomic read
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700270 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800271 public synchronized MastershipTerm getTermFor(DeviceId deviceId) {
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700272 if ((termMap.get(deviceId) == null)) {
273 return MastershipTerm.of(masterMap.get(deviceId), NOTHING);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700274 }
275 return MastershipTerm.of(
276 masterMap.get(deviceId), termMap.get(deviceId).get());
277 }
278
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700279 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800280 public synchronized MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700281 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800282 switch (role) {
283 case MASTER:
284 NodeId backup = reelect(deviceId, nodeId);
285 if (backup == null) {
286 // no master alternative
287 masterMap.remove(deviceId);
288 // TODO: Should there be new event type for no MASTER?
289 return new MastershipEvent(MASTER_CHANGED, deviceId,
290 getNodes(deviceId));
291 } else {
292 NodeId prevMaster = masterMap.put(deviceId, backup);
293 incrementTerm(deviceId);
294 addToBackup(deviceId, prevMaster);
295 return new MastershipEvent(MASTER_CHANGED, deviceId,
296 getNodes(deviceId));
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700297 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800298
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800299 case STANDBY:
300 case NONE:
301 boolean modified = addToBackup(deviceId, nodeId);
302 if (modified) {
303 return new MastershipEvent(BACKUPS_CHANGED, deviceId,
304 getNodes(deviceId));
305 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800306 break;
307
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800308 default:
309 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700310 }
311 return null;
312 }
313
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700314 //dumbly selects next-available node that's not the current one
315 //emulate leader election
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800316 private synchronized NodeId reelect(DeviceId did, NodeId nodeId) {
317 List<NodeId> stbys = backups.getOrDefault(did, Collections.emptyList());
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700318 NodeId backup = null;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800319 for (NodeId n : stbys) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700320 if (!n.equals(nodeId)) {
321 backup = n;
322 break;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700323 }
324 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800325 stbys.remove(backup);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700326 return backup;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700327 }
328
Ayaka Koshibec4047702014-10-07 14:43:52 -0700329 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800330 public synchronized MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700331 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800332 switch (role) {
333 case MASTER:
334 NodeId backup = reelect(deviceId, nodeId);
335 masterMap.put(deviceId, backup);
336 incrementTerm(deviceId);
337 return new MastershipEvent(MASTER_CHANGED, deviceId,
338 getNodes(deviceId));
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800339
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800340 case STANDBY:
341 if (removeFromBackups(deviceId, nodeId)) {
342 return new MastershipEvent(BACKUPS_CHANGED, deviceId,
343 getNodes(deviceId));
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700344 }
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800345 break;
346
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800347 case NONE:
Yuta HIGUCHI1b3f4db2014-11-18 11:04:31 -0800348 break;
349
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800350 default:
351 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700352 }
353 return null;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700354 }
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800355
356 @Override
357 public synchronized void relinquishAllRole(NodeId nodeId) {
358 List<MastershipEvent> events = new ArrayList<>();
359 Set<DeviceId> toRelinquish = new HashSet<>();
360
361 masterMap.entrySet().stream()
362 .filter(entry -> nodeId.equals(entry.getValue()))
363 .forEach(entry -> toRelinquish.add(entry.getKey()));
364
365 backups.entrySet().stream()
366 .filter(entry -> entry.getValue().contains(nodeId))
367 .forEach(entry -> toRelinquish.add(entry.getKey()));
368
369 toRelinquish.forEach(deviceId -> {
370 MastershipEvent event = relinquishRole(nodeId, deviceId);
371 if (event != null) {
372 events.add(event);
373 }
374 });
375
376 notifyDelegate(events);
377 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700378}