Ayaka Koshibe

Relinquish mastership, tests, and few modifications to trivial MastershipStore

Change-Id: Iae29de010f13cb3ee02bcb316510cc254d5756fc
...@@ -64,4 +64,14 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -64,4 +64,14 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
64 * @return the current master's ID and the term value for device, or null 64 * @return the current master's ID and the term value for device, or null
65 */ 65 */
66 MastershipTerm getTermFor(DeviceId deviceId); 66 MastershipTerm getTermFor(DeviceId deviceId);
67 +
68 + /**
69 + * Revokes a controller instance's mastership over a device and hands
70 + * over mastership to another controller instance.
71 + *
72 + * @param nodeId the controller instance identifier
73 + * @param deviceId device to revoke mastership for
74 + * @return a mastership event
75 + */
76 + MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId);
67 } 77 }
......
...@@ -72,9 +72,20 @@ public class MastershipManager ...@@ -72,9 +72,20 @@ public class MastershipManager
72 checkNotNull(nodeId, NODE_ID_NULL); 72 checkNotNull(nodeId, NODE_ID_NULL);
73 checkNotNull(deviceId, DEVICE_ID_NULL); 73 checkNotNull(deviceId, DEVICE_ID_NULL);
74 checkNotNull(role, ROLE_NULL); 74 checkNotNull(role, ROLE_NULL);
75 - //TODO figure out appropriate action for non-MASTER roles, if we even set those 75 +
76 - if (role.equals(MastershipRole.MASTER)) { 76 + MastershipRole current = store.getRole(nodeId, deviceId);
77 - MastershipEvent event = store.setMaster(nodeId, deviceId); 77 + if (role.equals(current)) {
78 + return;
79 + } else {
80 + MastershipEvent event = null;
81 + if (role.equals(MastershipRole.MASTER)) {
82 + //current was STANDBY, wanted MASTER
83 + event = store.setMaster(nodeId, deviceId);
84 + } else {
85 + //current was MASTER, wanted STANDBY
86 + event = store.unsetMaster(nodeId, deviceId);
87 + }
88 +
78 if (event != null) { 89 if (event != null) {
79 post(event); 90 post(event);
80 } 91 }
...@@ -90,7 +101,18 @@ public class MastershipManager ...@@ -90,7 +101,18 @@ public class MastershipManager
90 @Override 101 @Override
91 public void relinquishMastership(DeviceId deviceId) { 102 public void relinquishMastership(DeviceId deviceId) {
92 checkNotNull(deviceId, DEVICE_ID_NULL); 103 checkNotNull(deviceId, DEVICE_ID_NULL);
93 - // FIXME: add method to store to give up mastership and trigger new master selection process 104 +
105 + MastershipRole role = store.getRole(
106 + clusterService.getLocalNode().id(), deviceId);
107 + if (!role.equals(MastershipRole.MASTER)) {
108 + return;
109 + }
110 +
111 + MastershipEvent event = store.unsetMaster(
112 + clusterService.getLocalNode().id(), deviceId);
113 + if (event != null) {
114 + post(event);
115 + }
94 } 116 }
95 117
96 @Override 118 @Override
...@@ -159,10 +181,6 @@ public class MastershipManager ...@@ -159,10 +181,6 @@ public class MastershipManager
159 break; 181 break;
160 case INSTANCE_REMOVED: 182 case INSTANCE_REMOVED:
161 case INSTANCE_DEACTIVATED: 183 case INSTANCE_DEACTIVATED:
162 - for (DeviceId d : getDevicesOf(event.subject().id())) {
163 - //this method should be an admin iface?
164 - relinquishMastership(d);
165 - }
166 break; 184 break;
167 default: 185 default:
168 log.warn("unknown cluster event {}", event); 186 log.warn("unknown cluster event {}", event);
......
...@@ -204,8 +204,11 @@ public class DeviceManager ...@@ -204,8 +204,11 @@ public class DeviceManager
204 checkNotNull(deviceId, DEVICE_ID_NULL); 204 checkNotNull(deviceId, DEVICE_ID_NULL);
205 checkValidity(); 205 checkValidity();
206 DeviceEvent event = store.markOffline(deviceId); 206 DeviceEvent event = store.markOffline(deviceId);
207 +
208 + //we're no longer capable of mastership.
207 if (event != null) { 209 if (event != null) {
208 log.info("Device {} disconnected", deviceId); 210 log.info("Device {} disconnected", deviceId);
211 + mastershipService.relinquishMastership(deviceId);
209 post(event); 212 post(event);
210 } 213 }
211 } 214 }
......
...@@ -19,6 +19,7 @@ import org.onlab.onos.net.trivial.impl.SimpleMastershipStore; ...@@ -19,6 +19,7 @@ import org.onlab.onos.net.trivial.impl.SimpleMastershipStore;
19 import org.onlab.packet.IpPrefix; 19 import org.onlab.packet.IpPrefix;
20 20
21 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertEquals;
22 +import static org.junit.Assert.assertNull;
22 import static org.onlab.onos.net.MastershipRole.*; 23 import static org.onlab.onos.net.MastershipRole.*;
23 24
24 /** 25 /**
...@@ -65,7 +66,24 @@ public class MastershipManagerTest { ...@@ -65,7 +66,24 @@ public class MastershipManagerTest {
65 66
66 @Test 67 @Test
67 public void relinquishMastership() { 68 public void relinquishMastership() {
68 - //TODO 69 + //no backups - should turn to standby and no master for device
70 + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
71 + assertEquals("wrong role:", MASTER, mgr.getLocalRole(DEV_MASTER));
72 + mgr.relinquishMastership(DEV_MASTER);
73 + assertNull("wrong master:", mgr.getMasterFor(DEV_OTHER));
74 + assertEquals("wrong role:", STANDBY, mgr.getLocalRole(DEV_MASTER));
75 +
76 + //not master, nothing should happen
77 + mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY);
78 + mgr.relinquishMastership(DEV_OTHER);
79 + assertNull("wrong role:", mgr.getMasterFor(DEV_OTHER));
80 +
81 + //provide NID_OTHER as backup and relinquish
82 + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
83 + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_MASTER));
84 + mgr.setRole(NID_OTHER, DEV_MASTER, STANDBY);
85 + mgr.relinquishMastership(DEV_MASTER);
86 + assertEquals("wrong master:", NID_OTHER, mgr.getMasterFor(DEV_MASTER));
69 } 87 }
70 88
71 @Test 89 @Test
......
...@@ -122,4 +122,10 @@ public class DistributedMastershipStore ...@@ -122,4 +122,10 @@ public class DistributedMastershipStore
122 return null; 122 return null;
123 } 123 }
124 124
125 + @Override
126 + public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
127 + // TODO Auto-generated method stub
128 + return null;
129 + }
130 +
125 } 131 }
......
...@@ -5,6 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -5,6 +5,7 @@ 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;
8 import java.util.Map; 9 import java.util.Map;
9 import java.util.Set; 10 import java.util.Set;
10 import java.util.concurrent.atomic.AtomicInteger; 11 import java.util.concurrent.atomic.AtomicInteger;
...@@ -26,6 +27,8 @@ import org.onlab.onos.store.AbstractStore; ...@@ -26,6 +27,8 @@ import org.onlab.onos.store.AbstractStore;
26 import org.onlab.packet.IpPrefix; 27 import org.onlab.packet.IpPrefix;
27 import org.slf4j.Logger; 28 import org.slf4j.Logger;
28 29
30 +import com.google.common.collect.Lists;
31 +
29 import static org.onlab.onos.cluster.MastershipEvent.Type.*; 32 import static org.onlab.onos.cluster.MastershipEvent.Type.*;
30 33
31 /** 34 /**
...@@ -47,8 +50,10 @@ public class SimpleMastershipStore ...@@ -47,8 +50,10 @@ public class SimpleMastershipStore
47 50
48 //devices mapped to their masters, to emulate multiple nodes 51 //devices mapped to their masters, to emulate multiple nodes
49 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>(); 52 protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
53 + //emulate backups
54 + protected final Map<DeviceId, List<NodeId>> backupMap = new HashMap<>();
55 + //terms
50 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>(); 56 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
51 - protected final Set<NodeId> masters = new HashSet<>();
52 57
53 @Activate 58 @Activate
54 public void activate() { 59 public void activate() {
...@@ -63,24 +68,38 @@ public class SimpleMastershipStore ...@@ -63,24 +68,38 @@ public class SimpleMastershipStore
63 @Override 68 @Override
64 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { 69 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
65 70
66 - NodeId node = masterMap.get(deviceId); 71 + NodeId current = masterMap.get(deviceId);
67 - if (node == null) { 72 + List<NodeId> backups = backupMap.get(deviceId);
68 - synchronized (this) { 73 +
69 - masterMap.put(deviceId, nodeId); 74 + if (current == null) {
70 - termMap.put(deviceId, new AtomicInteger()); 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 + }
71 } 90 }
72 - return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); 91 + } else if (current.equals(nodeId)) {
73 - }
74 -
75 - if (node.equals(nodeId)) {
76 return null; 92 return null;
77 } else { 93 } else {
78 - synchronized (this) { 94 + //add current to backup, set master to new node
79 - masterMap.put(deviceId, nodeId); 95 + masterMap.put(deviceId, nodeId);
80 - termMap.get(deviceId).incrementAndGet(); 96 + backups.add(current);
81 - return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); 97 + backups.remove(nodeId);
82 - } 98 + termMap.get(deviceId).incrementAndGet();
83 } 99 }
100 +
101 + updateStandby(nodeId, deviceId);
102 + return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
84 } 103 }
85 104
86 @Override 105 @Override
...@@ -106,29 +125,97 @@ public class SimpleMastershipStore ...@@ -106,29 +125,97 @@ public class SimpleMastershipStore
106 125
107 @Override 126 @Override
108 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { 127 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
109 - NodeId node = masterMap.get(deviceId); 128 + NodeId current = masterMap.get(deviceId);
110 - MastershipRole role; 129 + List<NodeId> backups = backupMap.get(deviceId);
111 - if (node != null) { 130 +
112 - if (node.equals(nodeId)) { 131 + if (current == null) {
113 - role = MastershipRole.MASTER; 132 + //masterMap or backup doesn't contain device. Say new node is MASTER
114 - } else { 133 + if (backups == null) {
115 - role = MastershipRole.STANDBY; 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;
116 } 142 }
143 +
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;
117 } else { 155 } else {
118 - //masterMap doesn't contain it. 156 + //once created, a device never has a null backups list.
119 - role = MastershipRole.MASTER; 157 + if (!backups.contains(nodeId)) {
120 - masterMap.put(deviceId, 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 + }
121 } 165 }
122 - return role; 166 +
167 + return MastershipRole.STANDBY;
123 } 168 }
124 169
125 @Override 170 @Override
126 public MastershipTerm getTermFor(DeviceId deviceId) { 171 public MastershipTerm getTermFor(DeviceId deviceId) {
127 - if (masterMap.get(deviceId) == null) { 172 + if ((masterMap.get(deviceId) == null) ||
173 + (termMap.get(deviceId) == null)) {
128 return null; 174 return null;
129 } 175 }
130 return MastershipTerm.of( 176 return MastershipTerm.of(
131 masterMap.get(deviceId), termMap.get(deviceId).get()); 177 masterMap.get(deviceId), termMap.get(deviceId).get());
132 } 178 }
133 179
180 + @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 +
134 } 221 }
......