blob: 62c084e4aa9a351b3a32826b2f2e4a8bd5359050 [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 */
tomea961ff2014-10-01 12:45:15 -070016package org.onlab.onos.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;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070026import java.util.Set;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070027import java.util.concurrent.atomic.AtomicInteger;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070028
29import org.apache.felix.scr.annotations.Activate;
30import org.apache.felix.scr.annotations.Component;
31import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080032import org.apache.felix.scr.annotations.Reference;
33import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070034import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080035import org.onlab.onos.cluster.ClusterEventListener;
36import org.onlab.onos.cluster.ClusterService;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070037import org.onlab.onos.cluster.ControllerNode;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080038import org.onlab.onos.cluster.ControllerNode.State;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070039import org.onlab.onos.cluster.DefaultControllerNode;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070040import org.onlab.onos.cluster.NodeId;
Ayaka Koshibeabedb092014-10-20 17:01:31 -070041import org.onlab.onos.cluster.RoleInfo;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070042import org.onlab.onos.mastership.MastershipEvent;
43import org.onlab.onos.mastership.MastershipStore;
44import org.onlab.onos.mastership.MastershipStoreDelegate;
45import org.onlab.onos.mastership.MastershipTerm;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070046import org.onlab.onos.net.DeviceId;
47import org.onlab.onos.net.MastershipRole;
tomf80c9722014-09-24 14:49:18 -070048import org.onlab.onos.store.AbstractStore;
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070049import org.onlab.packet.IpAddress;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070050import org.slf4j.Logger;
51
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080052import com.google.common.collect.ImmutableList;
53import com.google.common.collect.ImmutableSet;
Ayaka Koshibefc981cf2014-10-21 12:44:17 -070054
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070055import static org.onlab.onos.mastership.MastershipEvent.Type.*;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070056
57/**
58 * Manages inventory of controller mastership over devices using
Ayaka Koshibe406d0102014-09-24 16:08:12 -070059 * trivial, non-distributed in-memory structures implementation.
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070060 */
61@Component(immediate = true)
62@Service
tomf80c9722014-09-24 14:49:18 -070063public class SimpleMastershipStore
64 extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
65 implements MastershipStore {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070066
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070067 private final Logger log = getLogger(getClass());
68
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -070069 private static final int NOTHING = 0;
70 private static final int INIT = 1;
71
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080072 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected ClusterService clusterService;
Ayaka Koshibe406d0102014-09-24 16:08:12 -070074
75 //devices mapped to their masters, to emulate multiple nodes
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070076 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070077 //emulate backups with pile of nodes
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080078 protected final Map<DeviceId, List<NodeId>> backups = new HashMap<>();
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070079 //terms
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070080 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070081
82 @Activate
83 public void activate() {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -080084 if (clusterService == null) {
85 // just for ease of unit test
86 final ControllerNode instance =
87 new DefaultControllerNode(new NodeId("local"),
88 IpAddress.valueOf("127.0.0.1"));
89
90 clusterService = new ClusterService() {
91
92 @Override
93 public ControllerNode getLocalNode() {
94 return instance;
95 }
96
97 @Override
98 public Set<ControllerNode> getNodes() {
99 return ImmutableSet.of(instance);
100 }
101
102 @Override
103 public ControllerNode getNode(NodeId nodeId) {
104 if (instance.id().equals(nodeId)) {
105 return instance;
106 }
107 return null;
108 }
109
110 @Override
111 public State getState(NodeId nodeId) {
112 if (instance.id().equals(nodeId)) {
113 return State.ACTIVE;
114 } else {
115 return State.INACTIVE;
116 }
117 }
118
119 @Override
120 public void addListener(ClusterEventListener listener) {
121 }
122
123 @Override
124 public void removeListener(ClusterEventListener listener) {
125 }
126 };
127 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700128 log.info("Started");
129 }
130
131 @Deactivate
132 public void deactivate() {
133 log.info("Stopped");
134 }
135
136 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800137 public synchronized MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700138
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800139 MastershipRole role = getRole(nodeId, deviceId);
140 switch (role) {
141 case MASTER:
142 // no-op
143 return null;
144 case STANDBY:
145 case NONE:
146 NodeId prevMaster = masterMap.put(deviceId, nodeId);
147 incrementTerm(deviceId);
148 removeFromBackups(deviceId, nodeId);
149 addToBackup(deviceId, prevMaster);
150 break;
151 default:
152 log.warn("unknown Mastership Role {}", role);
153 return null;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700154 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700155
Ayaka Koshibefc981cf2014-10-21 12:44:17 -0700156 return new MastershipEvent(MASTER_CHANGED, deviceId,
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800157 getNodes(deviceId));
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700158 }
159
160 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700161 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700162 return masterMap.get(deviceId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700163 }
164
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800165 // synchronized for atomic read
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700166 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800167 public synchronized RoleInfo getNodes(DeviceId deviceId) {
168 return new RoleInfo(masterMap.get(deviceId),
169 backups.getOrDefault(deviceId, ImmutableList.of()));
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700170 }
171
172 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700173 public Set<DeviceId> getDevices(NodeId nodeId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700174 Set<DeviceId> ids = new HashSet<>();
175 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
176 if (d.getValue().equals(nodeId)) {
177 ids.add(d.getKey());
178 }
179 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800180 return ids;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700181 }
182
183 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800184 public synchronized MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700185 //query+possible reelection
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800186 NodeId node = clusterService.getLocalNode().id();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700187 MastershipRole role = getRole(node, deviceId);
188
189 switch (role) {
190 case MASTER:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800191 return MastershipRole.MASTER;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700192 case STANDBY:
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800193 if (getMaster(deviceId) == null) {
194 // no master => become master
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700195 masterMap.put(deviceId, node);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800196 incrementTerm(deviceId);
197 // remove from backup list
198 removeFromBackups(deviceId, node);
199 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
200 getNodes(deviceId)));
201 return MastershipRole.MASTER;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700202 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800203 return MastershipRole.STANDBY;
204 case NONE:
205 if (getMaster(deviceId) == null) {
206 // no master => become master
207 masterMap.put(deviceId, node);
208 incrementTerm(deviceId);
209 notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId,
210 getNodes(deviceId)));
211 return MastershipRole.MASTER;
212 }
213 // add to backup list
214 if (addToBackup(deviceId, node)) {
215 notifyDelegate(new MastershipEvent(BACKUPS_CHANGED, deviceId,
216 getNodes(deviceId)));
217 }
218 return MastershipRole.STANDBY;
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700219 default:
220 log.warn("unknown Mastership Role {}", role);
221 }
222 return role;
tomb41d1ac2014-09-24 01:51:24 -0700223 }
224
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800225 // add to backup if not there already, silently ignores null node
226 private synchronized boolean addToBackup(DeviceId deviceId, NodeId nodeId) {
227 boolean modified = false;
228 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
229 if (nodeId != null && !stbys.contains(nodeId)) {
230 stbys.add(nodeId);
231 modified = true;
232 }
233 backups.put(deviceId, stbys);
234 return modified;
235 }
236
237 private synchronized boolean removeFromBackups(DeviceId deviceId, NodeId node) {
238 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
239 boolean modified = stbys.remove(node);
240 backups.put(deviceId, stbys);
241 return modified;
242 }
243
244 private synchronized void incrementTerm(DeviceId deviceId) {
245 AtomicInteger term = termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING));
246 term.incrementAndGet();
247 termMap.put(deviceId, term);
248 }
249
tomb41d1ac2014-09-24 01:51:24 -0700250 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700251 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700252 //just query
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700253 NodeId current = masterMap.get(deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700254 MastershipRole role;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700255
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800256 if (current != null && current.equals(nodeId)) {
257 return MastershipRole.MASTER;
258 }
259
260 if (backups.getOrDefault(deviceId, Collections.emptyList()).contains(nodeId)) {
261 role = MastershipRole.STANDBY;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700262 } else {
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800263 role = MastershipRole.NONE;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700264 }
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700265 return role;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700266 }
267
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800268 // synchronized for atomic read
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700269 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800270 public synchronized MastershipTerm getTermFor(DeviceId deviceId) {
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700271 if ((termMap.get(deviceId) == null)) {
272 return MastershipTerm.of(masterMap.get(deviceId), NOTHING);
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700273 }
274 return MastershipTerm.of(
275 masterMap.get(deviceId), termMap.get(deviceId).get());
276 }
277
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700278 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800279 public synchronized MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700280 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800281 switch (role) {
282 case MASTER:
283 NodeId backup = reelect(deviceId, nodeId);
284 if (backup == null) {
285 // no master alternative
286 masterMap.remove(deviceId);
287 // TODO: Should there be new event type for no MASTER?
288 return new MastershipEvent(MASTER_CHANGED, deviceId,
289 getNodes(deviceId));
290 } else {
291 NodeId prevMaster = masterMap.put(deviceId, backup);
292 incrementTerm(deviceId);
293 addToBackup(deviceId, prevMaster);
294 return new MastershipEvent(MASTER_CHANGED, deviceId,
295 getNodes(deviceId));
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700296 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800297 case STANDBY:
298 case NONE:
299 boolean modified = addToBackup(deviceId, nodeId);
300 if (modified) {
301 return new MastershipEvent(BACKUPS_CHANGED, deviceId,
302 getNodes(deviceId));
303 }
304 default:
305 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700306 }
307 return null;
308 }
309
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700310 //dumbly selects next-available node that's not the current one
311 //emulate leader election
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800312 private synchronized NodeId reelect(DeviceId did, NodeId nodeId) {
313 List<NodeId> stbys = backups.getOrDefault(did, Collections.emptyList());
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700314 NodeId backup = null;
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800315 for (NodeId n : stbys) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700316 if (!n.equals(nodeId)) {
317 backup = n;
318 break;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700319 }
320 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800321 stbys.remove(backup);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700322 return backup;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700323 }
324
Ayaka Koshibec4047702014-10-07 14:43:52 -0700325 @Override
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800326 public synchronized MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700327 MastershipRole role = getRole(nodeId, deviceId);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800328 switch (role) {
329 case MASTER:
330 NodeId backup = reelect(deviceId, nodeId);
331 masterMap.put(deviceId, backup);
332 incrementTerm(deviceId);
333 return new MastershipEvent(MASTER_CHANGED, deviceId,
334 getNodes(deviceId));
335 case STANDBY:
336 if (removeFromBackups(deviceId, nodeId)) {
337 return new MastershipEvent(BACKUPS_CHANGED, deviceId,
338 getNodes(deviceId));
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700339 }
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800340 case NONE:
341 default:
342 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700343 }
344 return null;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700345 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700346}