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