relinquishes mastership when device disconnects
Change-Id: I1aecc8862ce297569c358e1deb5ddc5fb52d5dd3
Showing
4 changed files
with
87 additions
and
101 deletions
... | @@ -78,16 +78,10 @@ implements MastershipService, MastershipAdminService { | ... | @@ -78,16 +78,10 @@ implements MastershipService, MastershipAdminService { |
78 | checkNotNull(deviceId, DEVICE_ID_NULL); | 78 | checkNotNull(deviceId, DEVICE_ID_NULL); |
79 | checkNotNull(role, ROLE_NULL); | 79 | checkNotNull(role, ROLE_NULL); |
80 | 80 | ||
81 | - MastershipRole current = store.getRole(nodeId, deviceId); | ||
82 | - if (role.equals(current)) { | ||
83 | - return; | ||
84 | - } else { | ||
85 | MastershipEvent event = null; | 81 | MastershipEvent event = null; |
86 | if (role.equals(MastershipRole.MASTER)) { | 82 | if (role.equals(MastershipRole.MASTER)) { |
87 | - //current was STANDBY, wanted MASTER | ||
88 | event = store.setMaster(nodeId, deviceId); | 83 | event = store.setMaster(nodeId, deviceId); |
89 | } else { | 84 | } else { |
90 | - //current was MASTER, wanted STANDBY | ||
91 | event = store.unsetMaster(nodeId, deviceId); | 85 | event = store.unsetMaster(nodeId, deviceId); |
92 | } | 86 | } |
93 | 87 | ||
... | @@ -95,7 +89,6 @@ implements MastershipService, MastershipAdminService { | ... | @@ -95,7 +89,6 @@ implements MastershipService, MastershipAdminService { |
95 | post(event); | 89 | post(event); |
96 | } | 90 | } |
97 | } | 91 | } |
98 | - } | ||
99 | 92 | ||
100 | @Override | 93 | @Override |
101 | public MastershipRole getLocalRole(DeviceId deviceId) { | 94 | public MastershipRole getLocalRole(DeviceId deviceId) { |
... | @@ -105,10 +98,7 @@ implements MastershipService, MastershipAdminService { | ... | @@ -105,10 +98,7 @@ implements MastershipService, MastershipAdminService { |
105 | 98 | ||
106 | @Override | 99 | @Override |
107 | public void relinquishMastership(DeviceId deviceId) { | 100 | public void relinquishMastership(DeviceId deviceId) { |
108 | - checkNotNull(deviceId, DEVICE_ID_NULL); | 101 | + MastershipRole role = getLocalRole(deviceId); |
109 | - | ||
110 | - MastershipRole role = store.getRole( | ||
111 | - clusterService.getLocalNode().id(), deviceId); | ||
112 | if (!role.equals(MastershipRole.MASTER)) { | 102 | if (!role.equals(MastershipRole.MASTER)) { |
113 | return; | 103 | return; |
114 | } | 104 | } | ... | ... |
... | @@ -202,7 +202,7 @@ public class DeviceManager | ... | @@ -202,7 +202,7 @@ public class DeviceManager |
202 | log.info("Device {} connected", deviceId); | 202 | log.info("Device {} connected", deviceId); |
203 | mastershipService.requestRoleFor(deviceId); | 203 | mastershipService.requestRoleFor(deviceId); |
204 | provider().roleChanged(event.subject(), | 204 | provider().roleChanged(event.subject(), |
205 | - mastershipService.getLocalRole(deviceId)); | 205 | + mastershipService.requestRoleFor(deviceId)); |
206 | post(event); | 206 | post(event); |
207 | } | 207 | } |
208 | } | 208 | } | ... | ... |
... | @@ -113,7 +113,6 @@ public class MastershipManagerTest { | ... | @@ -113,7 +113,6 @@ public class MastershipManagerTest { |
113 | mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); | 113 | mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); |
114 | mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY); | 114 | mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY); |
115 | assertEquals("should be one device:", 1, mgr.getDevicesOf(NID_LOCAL).size()); | 115 | assertEquals("should be one device:", 1, mgr.getDevicesOf(NID_LOCAL).size()); |
116 | - | ||
117 | //hand both devices to NID_LOCAL | 116 | //hand both devices to NID_LOCAL |
118 | mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER); | 117 | mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER); |
119 | assertEquals("should be two devices:", 2, mgr.getDevicesOf(NID_LOCAL).size()); | 118 | assertEquals("should be two devices:", 2, mgr.getDevicesOf(NID_LOCAL).size()); | ... | ... |
... | @@ -5,7 +5,6 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -5,7 +5,6 @@ import static org.slf4j.LoggerFactory.getLogger; |
5 | import java.util.Collections; | 5 | import java.util.Collections; |
6 | import java.util.HashMap; | 6 | import java.util.HashMap; |
7 | import java.util.HashSet; | 7 | import java.util.HashSet; |
8 | -import java.util.List; | ||
9 | import java.util.Map; | 8 | import java.util.Map; |
10 | import java.util.Set; | 9 | import java.util.Set; |
11 | import java.util.concurrent.atomic.AtomicInteger; | 10 | import java.util.concurrent.atomic.AtomicInteger; |
... | @@ -27,8 +26,6 @@ import org.onlab.onos.store.AbstractStore; | ... | @@ -27,8 +26,6 @@ import org.onlab.onos.store.AbstractStore; |
27 | import org.onlab.packet.IpPrefix; | 26 | import org.onlab.packet.IpPrefix; |
28 | import org.slf4j.Logger; | 27 | import org.slf4j.Logger; |
29 | 28 | ||
30 | -import com.google.common.collect.Lists; | ||
31 | - | ||
32 | import static org.onlab.onos.cluster.MastershipEvent.Type.*; | 29 | import static org.onlab.onos.cluster.MastershipEvent.Type.*; |
33 | 30 | ||
34 | /** | 31 | /** |
... | @@ -50,8 +47,8 @@ public class SimpleMastershipStore | ... | @@ -50,8 +47,8 @@ public class SimpleMastershipStore |
50 | 47 | ||
51 | //devices mapped to their masters, to emulate multiple nodes | 48 | //devices mapped to their masters, to emulate multiple nodes |
52 | protected final Map<DeviceId, NodeId> masterMap = new HashMap<>(); | 49 | protected final Map<DeviceId, NodeId> masterMap = new HashMap<>(); |
53 | - //emulate backups | 50 | + //emulate backups with pile of nodes |
54 | - protected final Map<DeviceId, List<NodeId>> backupMap = new HashMap<>(); | 51 | + protected final Set<NodeId> backups = new HashSet<>(); |
55 | //terms | 52 | //terms |
56 | protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>(); | 53 | protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>(); |
57 | 54 | ||
... | @@ -67,38 +64,28 @@ public class SimpleMastershipStore | ... | @@ -67,38 +64,28 @@ public class SimpleMastershipStore |
67 | 64 | ||
68 | @Override | 65 | @Override |
69 | public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { | 66 | public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { |
67 | + MastershipRole role = getRole(nodeId, deviceId); | ||
70 | 68 | ||
71 | - NodeId current = masterMap.get(deviceId); | ||
72 | - List<NodeId> backups = backupMap.get(deviceId); | ||
73 | - | ||
74 | - if (current == null) { | ||
75 | - if (backups == null) { | ||
76 | - //add new mapping to everything | ||
77 | synchronized (this) { | 69 | synchronized (this) { |
78 | - masterMap.put(deviceId, nodeId); | 70 | + switch (role) { |
79 | - backups = Lists.newLinkedList(); | 71 | + case MASTER: |
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)) { | ||
92 | return null; | 72 | return null; |
93 | - } else { | 73 | + case STANDBY: |
94 | - //add current to backup, set master to new node | ||
95 | masterMap.put(deviceId, nodeId); | 74 | masterMap.put(deviceId, nodeId); |
96 | - backups.add(current); | ||
97 | - backups.remove(nodeId); | ||
98 | termMap.get(deviceId).incrementAndGet(); | 75 | termMap.get(deviceId).incrementAndGet(); |
76 | + backups.add(nodeId); | ||
77 | + break; | ||
78 | + case NONE: | ||
79 | + masterMap.put(deviceId, nodeId); | ||
80 | + termMap.put(deviceId, new AtomicInteger()); | ||
81 | + backups.add(nodeId); | ||
82 | + break; | ||
83 | + default: | ||
84 | + log.warn("unknown Mastership Role {}", role); | ||
85 | + return null; | ||
86 | + } | ||
99 | } | 87 | } |
100 | 88 | ||
101 | - updateStandby(nodeId, deviceId); | ||
102 | return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); | 89 | return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); |
103 | } | 90 | } |
104 | 91 | ||
... | @@ -120,51 +107,61 @@ public class SimpleMastershipStore | ... | @@ -120,51 +107,61 @@ public class SimpleMastershipStore |
120 | 107 | ||
121 | @Override | 108 | @Override |
122 | public MastershipRole requestRole(DeviceId deviceId) { | 109 | public MastershipRole requestRole(DeviceId deviceId) { |
123 | - return getRole(instance.id(), deviceId); | 110 | + //query+possible reelection |
124 | - } | 111 | + NodeId node = instance.id(); |
125 | - | 112 | + MastershipRole role = getRole(node, deviceId); |
126 | - @Override | 113 | + |
127 | - public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { | 114 | + switch (role) { |
128 | - NodeId current = masterMap.get(deviceId); | 115 | + case MASTER: |
129 | - List<NodeId> backups = backupMap.get(deviceId); | 116 | + break; |
130 | - | 117 | + case STANDBY: |
131 | - if (current == null) { | ||
132 | - //masterMap or backup doesn't contain device. Say new node is MASTER | ||
133 | - if (backups == null) { | ||
134 | synchronized (this) { | 118 | synchronized (this) { |
135 | - masterMap.put(deviceId, nodeId); | 119 | + //try to "re-elect", since we're really not distributed |
136 | - backups = Lists.newLinkedList(); | 120 | + NodeId rel = reelect(node); |
137 | - backupMap.put(deviceId, backups); | 121 | + if (rel == null) { |
122 | + masterMap.put(deviceId, node); | ||
138 | termMap.put(deviceId, new AtomicInteger()); | 123 | termMap.put(deviceId, new AtomicInteger()); |
124 | + role = MastershipRole.MASTER; | ||
139 | } | 125 | } |
140 | - updateStandby(nodeId, deviceId); | 126 | + backups.add(node); |
141 | - return MastershipRole.MASTER; | ||
142 | } | 127 | } |
143 | - | 128 | + break; |
144 | - //device once existed, but got removed, and is now getting a backup. | 129 | + case NONE: |
145 | - if (!backups.contains(nodeId)) { | 130 | + //first to get to it, say we are master |
146 | synchronized (this) { | 131 | synchronized (this) { |
147 | - backups.add(nodeId); | 132 | + masterMap.put(deviceId, node); |
148 | termMap.put(deviceId, new AtomicInteger()); | 133 | termMap.put(deviceId, new AtomicInteger()); |
134 | + backups.add(node); | ||
135 | + role = MastershipRole.MASTER; | ||
136 | + } | ||
137 | + break; | ||
138 | + default: | ||
139 | + log.warn("unknown Mastership Role {}", role); | ||
149 | } | 140 | } |
150 | - updateStandby(nodeId, deviceId); | 141 | + return role; |
151 | } | 142 | } |
152 | 143 | ||
153 | - } else if (current.equals(nodeId)) { | 144 | + @Override |
154 | - return MastershipRole.MASTER; | 145 | + public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { |
146 | + //just query | ||
147 | + NodeId current = masterMap.get(deviceId); | ||
148 | + MastershipRole role; | ||
149 | + | ||
150 | + if (current == null) { | ||
151 | + //degenerate case - only node is its own backup | ||
152 | + if (backups.contains(nodeId)) { | ||
153 | + role = MastershipRole.STANDBY; | ||
155 | } else { | 154 | } else { |
156 | - //once created, a device never has a null backups list. | 155 | + role = MastershipRole.NONE; |
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 | } | 156 | } |
163 | - updateStandby(nodeId, deviceId); | 157 | + } else { |
158 | + if (current.equals(nodeId)) { | ||
159 | + role = MastershipRole.MASTER; | ||
160 | + } else { | ||
161 | + role = MastershipRole.STANDBY; | ||
164 | } | 162 | } |
165 | } | 163 | } |
166 | - | 164 | + return role; |
167 | - return MastershipRole.STANDBY; | ||
168 | } | 165 | } |
169 | 166 | ||
170 | @Override | 167 | @Override |
... | @@ -179,43 +176,43 @@ public class SimpleMastershipStore | ... | @@ -179,43 +176,43 @@ public class SimpleMastershipStore |
179 | 176 | ||
180 | @Override | 177 | @Override |
181 | public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) { | 178 | public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) { |
182 | - NodeId node = masterMap.get(deviceId); | 179 | + MastershipRole role = getRole(nodeId, deviceId); |
183 | - | ||
184 | - //TODO case where node is completely removed from the cluster? | ||
185 | - if (node.equals(nodeId)) { | ||
186 | synchronized (this) { | 180 | synchronized (this) { |
187 | - //pick new node. | 181 | + switch (role) { |
188 | - List<NodeId> backups = backupMap.get(deviceId); | 182 | + case MASTER: |
189 | - | 183 | + NodeId backup = reelect(nodeId); |
190 | - //no backups, so device is hosed | 184 | + if (backup == null) { |
191 | - if (backups.isEmpty()) { | ||
192 | masterMap.remove(deviceId); | 185 | masterMap.remove(deviceId); |
193 | - backups.add(nodeId); | 186 | + } else { |
194 | - return null; | ||
195 | - } | ||
196 | - NodeId backup = backups.remove(0); | ||
197 | masterMap.put(deviceId, backup); | 187 | masterMap.put(deviceId, backup); |
198 | - backups.add(nodeId); | 188 | + termMap.get(deviceId).incrementAndGet(); |
199 | return new MastershipEvent(MASTER_CHANGED, deviceId, backup); | 189 | return new MastershipEvent(MASTER_CHANGED, deviceId, backup); |
200 | } | 190 | } |
191 | + case STANDBY: | ||
192 | + case NONE: | ||
193 | + if (!termMap.containsKey(deviceId)) { | ||
194 | + termMap.put(deviceId, new AtomicInteger()); | ||
201 | } | 195 | } |
202 | - return null; | 196 | + backups.add(nodeId); |
197 | + break; | ||
198 | + default: | ||
199 | + log.warn("unknown Mastership Role {}", role); | ||
203 | } | 200 | } |
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 | } | 201 | } |
212 | - synchronized (this) { | 202 | + return null; |
213 | - List<NodeId> nodes = e.getValue(); | ||
214 | - if (!nodes.contains(nodeId)) { | ||
215 | - nodes.add(nodeId); | ||
216 | } | 203 | } |
204 | + | ||
205 | + //dumbly selects next-available node that's not the current one | ||
206 | + //emulate leader election | ||
207 | + private NodeId reelect(NodeId nodeId) { | ||
208 | + NodeId backup = null; | ||
209 | + for (NodeId n : backups) { | ||
210 | + if (!n.equals(nodeId)) { | ||
211 | + backup = n; | ||
212 | + break; | ||
217 | } | 213 | } |
218 | } | 214 | } |
215 | + return backup; | ||
219 | } | 216 | } |
220 | 217 | ||
221 | } | 218 | } | ... | ... |
-
Please register or login to post a comment