blob: c5daf6c5763e9c07fd4b9de47994208d49eb96e6 [file] [log] [blame]
Yuta HIGUCHI80912e62014-10-12 00:15:47 -07001package org.onlab.onos.store.mastership.impl;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -07002
3import static org.junit.Assert.assertEquals;
4import static org.junit.Assert.assertNull;
5import static org.junit.Assert.assertTrue;
6import static org.onlab.onos.net.MastershipRole.*;
7
Ayaka Koshibec4047702014-10-07 14:43:52 -07008import java.util.Map;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -07009import java.util.Set;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070010import java.util.concurrent.CountDownLatch;
11import java.util.concurrent.TimeUnit;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070012
13import org.junit.After;
14import org.junit.AfterClass;
15import org.junit.Before;
16import org.junit.BeforeClass;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070017import org.junit.Ignore;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070018import org.junit.Test;
19import org.onlab.onos.cluster.ClusterEventListener;
20import org.onlab.onos.cluster.ClusterService;
21import org.onlab.onos.cluster.ControllerNode;
22import org.onlab.onos.cluster.ControllerNode.State;
23import org.onlab.onos.cluster.DefaultControllerNode;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070024import org.onlab.onos.cluster.NodeId;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070025import org.onlab.onos.mastership.MastershipEvent;
26import org.onlab.onos.mastership.MastershipStoreDelegate;
27import org.onlab.onos.mastership.MastershipTerm;
28import org.onlab.onos.mastership.MastershipEvent.Type;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070029import org.onlab.onos.net.DeviceId;
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070030import org.onlab.onos.net.MastershipRole;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070031import org.onlab.onos.store.common.StoreManager;
32import org.onlab.onos.store.common.StoreService;
33import org.onlab.onos.store.common.TestStoreManager;
Ayaka Koshibe4c891272014-10-08 17:14:16 -070034import org.onlab.onos.store.serializers.KryoSerializer;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070035import org.onlab.packet.IpPrefix;
36
37import com.google.common.collect.Sets;
38import com.hazelcast.config.Config;
39import com.hazelcast.core.Hazelcast;
40
41/**
42 * Test of the Hazelcast-based distributed MastershipStore implementation.
43 */
44public class DistributedMastershipStoreTest {
45
46 private static final DeviceId DID1 = DeviceId.deviceId("of:01");
47 private static final DeviceId DID2 = DeviceId.deviceId("of:02");
48 private static final DeviceId DID3 = DeviceId.deviceId("of:03");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070049
50 private static final IpPrefix IP = IpPrefix.valueOf("127.0.0.1");
51
52 private static final NodeId N1 = new NodeId("node1");
53 private static final NodeId N2 = new NodeId("node2");
54
55 private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
56 private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
57
58 private DistributedMastershipStore dms;
59 private TestDistributedMastershipStore testStore;
Ayaka Koshibe4c891272014-10-08 17:14:16 -070060 private KryoSerializer serializationMgr;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070061 private StoreManager storeMgr;
62
63 @BeforeClass
64 public static void setUpBeforeClass() throws Exception {
65 }
66
67 @AfterClass
68 public static void tearDownAfterClass() throws Exception {
69 }
70
71 @Before
72 public void setUp() throws Exception {
73 // TODO should find a way to clean Hazelcast instance without shutdown.
74 Config config = TestStoreManager.getTestConfig();
75
76 storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
77 storeMgr.activate();
78
Ayaka Koshibe4c891272014-10-08 17:14:16 -070079 serializationMgr = new KryoSerializer();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070080
81 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
82 dms.clusterService = new TestClusterService();
83 dms.activate();
84
85 testStore = (TestDistributedMastershipStore) dms;
86 }
87
88 @After
89 public void tearDown() throws Exception {
90 dms.deactivate();
91
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070092 storeMgr.deactivate();
93 }
94
95 @Test
96 public void getRole() {
97 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
Ayaka Koshibec4047702014-10-07 14:43:52 -070098 testStore.put(DID1, N1, true, false, true);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070099 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700100 testStore.put(DID1, N2, false, true, false);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700101 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
102 }
103
104 @Test
105 public void getMaster() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700106 assertTrue("wrong store state:", dms.roleMap.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700107
108 testStore.put(DID1, N1, true, false, false);
109 assertEquals("wrong master:", N1, dms.getMaster(DID1));
110 assertNull("wrong master:", dms.getMaster(DID2));
111 }
112
113 @Test
114 public void getDevices() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700115 assertTrue("wrong store state:", dms.roleMap.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700116
117 testStore.put(DID1, N1, true, false, false);
118 testStore.put(DID2, N1, true, false, false);
119 testStore.put(DID3, N2, true, false, false);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700120 assertEquals("wrong devices",
121 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
122 }
123
124 @Test
125 public void requestRoleAndTerm() {
126 //CN1 is "local"
127 testStore.setCurrent(CN1);
128
129 //if already MASTER, nothing should happen
130 testStore.put(DID2, N1, true, false, false);
131 assertEquals("wrong role for MASTER:", MASTER, dms.requestRole(DID2));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700132
133 //populate maps with DID1, N1 thru NONE case
134 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700135 assertTrue("wrong state for store:", !dms.terms.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700136 assertEquals("wrong term",
137 MastershipTerm.of(N1, 0), dms.getTermFor(DID1));
138
139 //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
140 testStore.setCurrent(CN2);
141 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700142 assertEquals("wrong number of entries:", 2, dms.terms.size());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700143
144 //change term and requestRole() again; should persist
145 testStore.increment(DID2);
146 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
147 assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
148 }
149
150 @Test
151 public void setMaster() {
152 //populate maps with DID1, N1 as MASTER thru NONE case
153 testStore.setCurrent(CN1);
154 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
155 assertNull("wrong event:", dms.setMaster(N1, DID1));
156
157 //switch over to N2
158 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700159 System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700160 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
161
162 //orphan switch - should be rare case
163 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
164 assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
165 //disconnect and reconnect - sign of failing re-election or single-instance channel
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700166 dms.roleMap.clear();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700167 dms.setMaster(N2, DID2);
168 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
169 }
170
171 @Test
Ayaka Koshibec4047702014-10-07 14:43:52 -0700172 public void relinquishRole() {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700173 //populate maps with DID1, N1 as MASTER thru NONE case
174 testStore.setCurrent(CN1);
175 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
176 //no backup, no new MASTER/event
Ayaka Koshibec4047702014-10-07 14:43:52 -0700177 assertNull("wrong event:", dms.relinquishRole(N1, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700178
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700179 dms.requestRole(DID1);
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700180
181 //add backup CN2, get it elected MASTER by relinquishing
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700182 testStore.setCurrent(CN2);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700183 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
184 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700185 assertEquals("wrong master", N2, dms.getMaster(DID1));
186
Ayaka Koshibec4047702014-10-07 14:43:52 -0700187 //all nodes "give up" on device, which goes back to NONE.
188 assertNull("wrong event:", dms.relinquishRole(N2, DID1));
189 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700190
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700191 assertEquals("wrong number of retired nodes", 2,
192 dms.roleMap.get(DID1).nodesOfRole(NONE).size());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700193
194 //bring nodes back
195 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
196 testStore.setCurrent(CN1);
197 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700198 assertEquals("wrong number of backup nodes", 1,
199 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700200
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700201 //If STANDBY, should drop to NONE
202 assertNull("wrong event:", dms.relinquishRole(N1, DID1));
203 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
204
Ayaka Koshibec4047702014-10-07 14:43:52 -0700205 //NONE - nothing happens
206 assertNull("wrong event:", dms.relinquishRole(N1, DID2));
207 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700208
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700209 }
210
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700211 @Ignore("Ignore until Delegate spec. is clear.")
212 @Test
213 public void testEvents() throws InterruptedException {
214 //shamelessly copy other distributed store tests
215 final CountDownLatch addLatch = new CountDownLatch(1);
216
217 MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
218 @Override
219 public void notify(MastershipEvent event) {
220 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
221 assertEquals("wrong subject", DID1, event.subject());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700222 assertEquals("wrong subject", N1, event.roleInfo().master());
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700223 addLatch.countDown();
224 }
225 };
226
227 dms.setDelegate(checkAdd);
228 dms.setMaster(N1, DID1);
229 //this will fail until we do something about single-instance-ness
230 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
231 }
232
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700233 private class TestDistributedMastershipStore extends
234 DistributedMastershipStore {
235 public TestDistributedMastershipStore(StoreService storeService,
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700236 KryoSerializer kryoSerialization) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700237 this.storeService = storeService;
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700238 this.serializer = kryoSerialization;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700239 }
240
241 //helper to populate master/backup structures
242 public void put(DeviceId dev, NodeId node,
Ayaka Koshibec4047702014-10-07 14:43:52 -0700243 boolean master, boolean backup, boolean term) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700244 RoleValue rv = dms.roleMap.get(dev);
245 if (rv == null) {
246 rv = new RoleValue();
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700247 }
Ayaka Koshibec4047702014-10-07 14:43:52 -0700248
249 if (master) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700250 rv.add(MASTER, node);
251 rv.reassign(node, STANDBY, NONE);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700252 }
253 if (backup) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700254 rv.add(STANDBY, node);
255 rv.remove(MASTER, node);
256 rv.remove(NONE, node);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700257 }
258 if (term) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700259 dms.terms.put(dev, 0);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700260 }
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700261 dms.roleMap.put(dev, rv);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700262 }
263
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700264 //a dumb utility function.
Ayaka Koshibec4047702014-10-07 14:43:52 -0700265 public void dump() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700266 for (Map.Entry<DeviceId, RoleValue> el : dms.roleMap.entrySet()) {
267 System.out.println("DID: " + el.getKey());
268 for (MastershipRole role : MastershipRole.values()) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700269 System.out.println("\t" + role.toString() + ":");
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700270 for (NodeId n : el.getValue().nodesOfRole(role)) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700271 System.out.println("\t\t" + n);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700272 }
273 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700274 }
275 }
276
277 //increment term for a device
278 public void increment(DeviceId dev) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700279 Integer t = dms.terms.get(dev);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700280 if (t != null) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700281 dms.terms.put(dev, ++t);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700282 }
283 }
284
285 //sets the "local" node
286 public void setCurrent(ControllerNode node) {
287 ((TestClusterService) clusterService).current = node;
288 }
289 }
290
291 private class TestClusterService implements ClusterService {
292
293 protected ControllerNode current;
294
295 @Override
296 public ControllerNode getLocalNode() {
297 return current;
298 }
299
300 @Override
301 public Set<ControllerNode> getNodes() {
302 return Sets.newHashSet(CN1, CN2);
303 }
304
305 @Override
306 public ControllerNode getNode(NodeId nodeId) {
307 return null;
308 }
309
310 @Override
311 public State getState(NodeId nodeId) {
312 return null;
313 }
314
315 @Override
316 public void addListener(ClusterEventListener listener) {
317 }
318
319 @Override
320 public void removeListener(ClusterEventListener listener) {
321 }
322
323 }
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700324
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700325}