blob: 33da98798aef1a0814dddfcda05f2ec68a855b87 [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;
32import org.apache.felix.scr.annotations.Service;
33import org.onlab.onos.cluster.ControllerNode;
34import org.onlab.onos.cluster.DefaultControllerNode;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070035import org.onlab.onos.cluster.NodeId;
Ayaka Koshibeabedb092014-10-20 17:01:31 -070036import org.onlab.onos.cluster.RoleInfo;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070037import org.onlab.onos.mastership.MastershipEvent;
38import org.onlab.onos.mastership.MastershipStore;
39import org.onlab.onos.mastership.MastershipStoreDelegate;
40import org.onlab.onos.mastership.MastershipTerm;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070041import org.onlab.onos.net.DeviceId;
42import org.onlab.onos.net.MastershipRole;
tomf80c9722014-09-24 14:49:18 -070043import org.onlab.onos.store.AbstractStore;
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070044import org.onlab.packet.IpAddress;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070045import org.slf4j.Logger;
46
Ayaka Koshibefc981cf2014-10-21 12:44:17 -070047import com.google.common.collect.Lists;
48
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070049import static org.onlab.onos.mastership.MastershipEvent.Type.*;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070050
51/**
52 * Manages inventory of controller mastership over devices using
Ayaka Koshibe406d0102014-09-24 16:08:12 -070053 * trivial, non-distributed in-memory structures implementation.
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070054 */
55@Component(immediate = true)
56@Service
tomf80c9722014-09-24 14:49:18 -070057public class SimpleMastershipStore
58 extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
59 implements MastershipStore {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070060
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070061 private final Logger log = getLogger(getClass());
62
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070063 public static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1");
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070064
Ayaka Koshibe406d0102014-09-24 16:08:12 -070065 private ControllerNode instance =
66 new DefaultControllerNode(new NodeId("local"), LOCALHOST);
67
68 //devices mapped to their masters, to emulate multiple nodes
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070069 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070070 //emulate backups with pile of nodes
71 protected final Set<NodeId> backups = new HashSet<>();
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070072 //terms
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070073 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070074
75 @Activate
76 public void activate() {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070077 log.info("Started");
78 }
79
80 @Deactivate
81 public void deactivate() {
82 log.info("Stopped");
83 }
84
85 @Override
Ayaka Koshibe406d0102014-09-24 16:08:12 -070086 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070087 MastershipRole role = getRole(nodeId, deviceId);
Ayaka Koshibe406d0102014-09-24 16:08:12 -070088
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070089 synchronized (this) {
90 switch (role) {
91 case MASTER:
92 return null;
93 case STANDBY:
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070094 masterMap.put(deviceId, nodeId);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070095 termMap.get(deviceId).incrementAndGet();
Ayaka Koshibe971a38a2014-09-30 11:56:23 -070096 backups.add(nodeId);
97 break;
98 case NONE:
99 masterMap.put(deviceId, nodeId);
100 termMap.put(deviceId, new AtomicInteger());
101 backups.add(nodeId);
102 break;
103 default:
104 log.warn("unknown Mastership Role {}", role);
105 return null;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700106 }
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700107 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700108
Ayaka Koshibefc981cf2014-10-21 12:44:17 -0700109 return new MastershipEvent(MASTER_CHANGED, deviceId,
110 new RoleInfo(nodeId, Lists.newLinkedList(backups)));
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700111 }
112
113 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700114 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700115 return masterMap.get(deviceId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700116 }
117
118 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700119 public RoleInfo getNodes(DeviceId deviceId) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700120 List<NodeId> nodes = new ArrayList<>();
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700121 nodes.addAll(backups);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700122
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700123 return new RoleInfo(masterMap.get(deviceId), nodes);
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700124 }
125
126 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700127 public Set<DeviceId> getDevices(NodeId nodeId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700128 Set<DeviceId> ids = new HashSet<>();
129 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
130 if (d.getValue().equals(nodeId)) {
131 ids.add(d.getKey());
132 }
133 }
134 return Collections.unmodifiableSet(ids);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700135 }
136
137 @Override
tomb41d1ac2014-09-24 01:51:24 -0700138 public MastershipRole requestRole(DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700139 //query+possible reelection
140 NodeId node = instance.id();
141 MastershipRole role = getRole(node, deviceId);
142
143 switch (role) {
144 case MASTER:
145 break;
146 case STANDBY:
147 synchronized (this) {
148 //try to "re-elect", since we're really not distributed
149 NodeId rel = reelect(node);
150 if (rel == null) {
151 masterMap.put(deviceId, node);
152 termMap.put(deviceId, new AtomicInteger());
153 role = MastershipRole.MASTER;
154 }
155 backups.add(node);
156 }
157 break;
158 case NONE:
159 //first to get to it, say we are master
160 synchronized (this) {
161 masterMap.put(deviceId, node);
162 termMap.put(deviceId, new AtomicInteger());
163 backups.add(node);
164 role = MastershipRole.MASTER;
165 }
166 break;
167 default:
168 log.warn("unknown Mastership Role {}", role);
169 }
170 return role;
tomb41d1ac2014-09-24 01:51:24 -0700171 }
172
173 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700174 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700175 //just query
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700176 NodeId current = masterMap.get(deviceId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700177 MastershipRole role;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700178
179 if (current == null) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700180 if (backups.contains(nodeId)) {
181 role = MastershipRole.STANDBY;
182 } else {
183 role = MastershipRole.NONE;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700184 }
185 } else {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700186 if (current.equals(nodeId)) {
187 role = MastershipRole.MASTER;
188 } else {
189 role = MastershipRole.STANDBY;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700190 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700191 }
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700192 return role;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700193 }
194
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700195 @Override
196 public MastershipTerm getTermFor(DeviceId deviceId) {
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700197 if ((masterMap.get(deviceId) == null) ||
198 (termMap.get(deviceId) == null)) {
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700199 return null;
200 }
201 return MastershipTerm.of(
202 masterMap.get(deviceId), termMap.get(deviceId).get());
203 }
204
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700205 @Override
Ayaka Koshibec4047702014-10-07 14:43:52 -0700206 public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700207 MastershipRole role = getRole(nodeId, deviceId);
208 synchronized (this) {
209 switch (role) {
210 case MASTER:
211 NodeId backup = reelect(nodeId);
212 if (backup == null) {
213 masterMap.remove(deviceId);
214 } else {
215 masterMap.put(deviceId, backup);
216 termMap.get(deviceId).incrementAndGet();
Ayaka Koshibefc981cf2014-10-21 12:44:17 -0700217 return new MastershipEvent(MASTER_CHANGED, deviceId,
218 new RoleInfo(backup, Lists.newLinkedList(backups)));
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700219 }
220 case STANDBY:
221 case NONE:
222 if (!termMap.containsKey(deviceId)) {
223 termMap.put(deviceId, new AtomicInteger());
224 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700225 backups.add(nodeId);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700226 break;
227 default:
228 log.warn("unknown Mastership Role {}", role);
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700229 }
230 }
231 return null;
232 }
233
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700234 //dumbly selects next-available node that's not the current one
235 //emulate leader election
236 private NodeId reelect(NodeId nodeId) {
237 NodeId backup = null;
238 for (NodeId n : backups) {
239 if (!n.equals(nodeId)) {
240 backup = n;
241 break;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700242 }
243 }
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700244 backups.remove(backup);
Ayaka Koshibe971a38a2014-09-30 11:56:23 -0700245 return backup;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700246 }
247
Ayaka Koshibec4047702014-10-07 14:43:52 -0700248 @Override
249 public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibeb62aab52014-10-24 13:15:25 -0700250 MastershipRole role = getRole(nodeId, deviceId);
251 synchronized (this) {
252 switch (role) {
253 case MASTER:
254 NodeId backup = reelect(nodeId);
255 backups.remove(nodeId);
256 if (backup == null) {
257 masterMap.remove(deviceId);
258 } else {
259 masterMap.put(deviceId, backup);
260 termMap.get(deviceId).incrementAndGet();
261 return new MastershipEvent(MASTER_CHANGED, deviceId,
262 new RoleInfo(backup, Lists.newLinkedList(backups)));
263 }
264 case STANDBY:
265 backups.remove(nodeId);
266 case NONE:
267 default:
268 log.warn("unknown Mastership Role {}", role);
269 }
270 }
271 return null;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700272 }
273
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700274}