blob: 669070744099ab0bf075b53a09588cba681a123d [file] [log] [blame]
Ayaka Koshibea7f044e2014-09-23 16:56:20 -07001package org.onlab.onos.net.trivial.impl;
2
3import static org.slf4j.LoggerFactory.getLogger;
4
5import java.util.Collections;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -07006import java.util.HashMap;
Ayaka Koshibe406d0102014-09-24 16:08:12 -07007import java.util.HashSet;
Ayaka Koshibed9f693e2014-09-29 18:04:54 -07008import java.util.List;
Ayaka Koshibe406d0102014-09-24 16:08:12 -07009import java.util.Map;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070010import java.util.Set;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070011import java.util.concurrent.atomic.AtomicInteger;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070012
13import org.apache.felix.scr.annotations.Activate;
14import org.apache.felix.scr.annotations.Component;
15import org.apache.felix.scr.annotations.Deactivate;
16import org.apache.felix.scr.annotations.Service;
17import org.onlab.onos.cluster.ControllerNode;
18import org.onlab.onos.cluster.DefaultControllerNode;
19import org.onlab.onos.cluster.MastershipEvent;
20import org.onlab.onos.cluster.MastershipStore;
tomf80c9722014-09-24 14:49:18 -070021import org.onlab.onos.cluster.MastershipStoreDelegate;
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070022import org.onlab.onos.cluster.MastershipTerm;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070023import org.onlab.onos.cluster.NodeId;
24import org.onlab.onos.net.DeviceId;
25import org.onlab.onos.net.MastershipRole;
tomf80c9722014-09-24 14:49:18 -070026import org.onlab.onos.store.AbstractStore;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070027import org.onlab.packet.IpPrefix;
28import org.slf4j.Logger;
29
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070030import com.google.common.collect.Lists;
31
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070032import static org.onlab.onos.cluster.MastershipEvent.Type.*;
33
34/**
35 * Manages inventory of controller mastership over devices using
Ayaka Koshibe406d0102014-09-24 16:08:12 -070036 * trivial, non-distributed in-memory structures implementation.
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070037 */
38@Component(immediate = true)
39@Service
tomf80c9722014-09-24 14:49:18 -070040public class SimpleMastershipStore
41 extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
42 implements MastershipStore {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070043
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070044 private final Logger log = getLogger(getClass());
45
Ayaka Koshibe406d0102014-09-24 16:08:12 -070046 public static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1");
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070047
Ayaka Koshibe406d0102014-09-24 16:08:12 -070048 private ControllerNode instance =
49 new DefaultControllerNode(new NodeId("local"), LOCALHOST);
50
51 //devices mapped to their masters, to emulate multiple nodes
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070052 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070053 //emulate backups
54 protected final Map<DeviceId, List<NodeId>> backupMap = new HashMap<>();
55 //terms
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -070056 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070057
58 @Activate
59 public void activate() {
Ayaka Koshibea7f044e2014-09-23 16:56:20 -070060 log.info("Started");
61 }
62
63 @Deactivate
64 public void deactivate() {
65 log.info("Stopped");
66 }
67
68 @Override
Ayaka Koshibe406d0102014-09-24 16:08:12 -070069 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
70
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070071 NodeId current = masterMap.get(deviceId);
72 List<NodeId> backups = backupMap.get(deviceId);
Ayaka Koshibe406d0102014-09-24 16:08:12 -070073
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070074 if (current == null) {
75 if (backups == null) {
76 //add new mapping to everything
77 synchronized (this) {
78 masterMap.put(deviceId, nodeId);
79 backups = Lists.newLinkedList();
80 backupMap.put(deviceId, backups);
81 termMap.put(deviceId, new AtomicInteger());
82 }
83 } else {
84 //set master to new node and remove from backups if there
85 synchronized (this) {
86 masterMap.put(deviceId, nodeId);
87 backups.remove(nodeId);
88 termMap.get(deviceId).incrementAndGet();
89 }
90 }
91 } else if (current.equals(nodeId)) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -070092 return null;
93 } else {
Ayaka Koshibed9f693e2014-09-29 18:04:54 -070094 //add current to backup, set master to new node
95 masterMap.put(deviceId, nodeId);
96 backups.add(current);
97 backups.remove(nodeId);
98 termMap.get(deviceId).incrementAndGet();
Ayaka Koshibe406d0102014-09-24 16:08:12 -070099 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700100
101 updateStandby(nodeId, deviceId);
102 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700103 }
104
105 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700106 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700107 return masterMap.get(deviceId);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700108 }
109
110 @Override
111 public Set<DeviceId> getDevices(NodeId nodeId) {
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700112 Set<DeviceId> ids = new HashSet<>();
113 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
114 if (d.getValue().equals(nodeId)) {
115 ids.add(d.getKey());
116 }
117 }
118 return Collections.unmodifiableSet(ids);
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700119 }
120
121 @Override
tomb41d1ac2014-09-24 01:51:24 -0700122 public MastershipRole requestRole(DeviceId deviceId) {
123 return getRole(instance.id(), deviceId);
124 }
125
126 @Override
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700127 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700128 NodeId current = masterMap.get(deviceId);
129 List<NodeId> backups = backupMap.get(deviceId);
130
131 if (current == null) {
132 //masterMap or backup doesn't contain device. Say new node is MASTER
133 if (backups == null) {
134 synchronized (this) {
135 masterMap.put(deviceId, nodeId);
136 backups = Lists.newLinkedList();
137 backupMap.put(deviceId, backups);
138 termMap.put(deviceId, new AtomicInteger());
139 }
140 updateStandby(nodeId, deviceId);
141 return MastershipRole.MASTER;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700142 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700143
144 //device once existed, but got removed, and is now getting a backup.
145 if (!backups.contains(nodeId)) {
146 synchronized (this) {
147 backups.add(nodeId);
148 termMap.put(deviceId, new AtomicInteger());
149 }
150 updateStandby(nodeId, deviceId);
151 }
152
153 } else if (current.equals(nodeId)) {
154 return MastershipRole.MASTER;
Ayaka Koshibe406d0102014-09-24 16:08:12 -0700155 } else {
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700156 //once created, a device never has a null backups list.
157 if (!backups.contains(nodeId)) {
158 //we must have requested STANDBY setting
159 synchronized (this) {
160 backups.add(nodeId);
161 termMap.put(deviceId, new AtomicInteger());
162 }
163 updateStandby(nodeId, deviceId);
164 }
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700165 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700166
167 return MastershipRole.STANDBY;
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700168 }
169
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700170 @Override
171 public MastershipTerm getTermFor(DeviceId deviceId) {
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700172 if ((masterMap.get(deviceId) == null) ||
173 (termMap.get(deviceId) == null)) {
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700174 return null;
175 }
176 return MastershipTerm.of(
177 masterMap.get(deviceId), termMap.get(deviceId).get());
178 }
179
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700180 @Override
181 public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
182 NodeId node = masterMap.get(deviceId);
183
184 //TODO case where node is completely removed from the cluster?
185 if (node.equals(nodeId)) {
186 synchronized (this) {
187 //pick new node.
188 List<NodeId> backups = backupMap.get(deviceId);
189
190 //no backups, so device is hosed
191 if (backups.isEmpty()) {
192 masterMap.remove(deviceId);
193 backups.add(nodeId);
194 return null;
195 }
196 NodeId backup = backups.remove(0);
197 masterMap.put(deviceId, backup);
198 backups.add(nodeId);
199 return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
200 }
201 }
202 return null;
203 }
204
205 //add node as STANDBY to maps un-scalably.
206 private void updateStandby(NodeId nodeId, DeviceId deviceId) {
207 for (Map.Entry<DeviceId, List<NodeId>> e : backupMap.entrySet()) {
208 DeviceId dev = e.getKey();
209 if (dev.equals(deviceId)) {
210 continue;
211 }
212 synchronized (this) {
213 List<NodeId> nodes = e.getValue();
214 if (!nodes.contains(nodeId)) {
215 nodes.add(nodeId);
216 }
217 }
218 }
219 }
220
Ayaka Koshibea7f044e2014-09-23 16:56:20 -0700221}