blob: c3efdfe92eab42b2a42fe61293a7267be7b194e0 [file] [log] [blame]
Ayaka Koshibe8583ff32014-10-02 16:25:30 -07001package org.onlab.onos.store.cluster.impl;
2
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 Koshibe25fd23a2014-10-03 15:50:43 -07008import java.util.List;
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 Koshibe5c0f2372014-10-02 17:59:04 -070024import org.onlab.onos.cluster.MastershipEvent;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070025import org.onlab.onos.cluster.MastershipEvent.Type;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070026import org.onlab.onos.cluster.MastershipStoreDelegate;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070027import org.onlab.onos.cluster.MastershipTerm;
28import org.onlab.onos.cluster.NodeId;
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -070029import org.onlab.onos.net.Device;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070030import org.onlab.onos.net.DeviceId;
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -070031import org.onlab.onos.net.MastershipRole;
32import org.onlab.onos.net.Port;
33import org.onlab.onos.net.PortNumber;
34import org.onlab.onos.net.device.DeviceListener;
35import org.onlab.onos.net.device.DeviceService;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070036import org.onlab.onos.store.common.StoreManager;
37import org.onlab.onos.store.common.StoreService;
38import org.onlab.onos.store.common.TestStoreManager;
39import org.onlab.onos.store.serializers.KryoSerializationManager;
40import org.onlab.onos.store.serializers.KryoSerializationService;
41import org.onlab.packet.IpPrefix;
42
43import com.google.common.collect.Sets;
44import com.hazelcast.config.Config;
45import com.hazelcast.core.Hazelcast;
46
47/**
48 * Test of the Hazelcast-based distributed MastershipStore implementation.
49 */
50public class DistributedMastershipStoreTest {
51
52 private static final DeviceId DID1 = DeviceId.deviceId("of:01");
53 private static final DeviceId DID2 = DeviceId.deviceId("of:02");
54 private static final DeviceId DID3 = DeviceId.deviceId("of:03");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070055
56 private static final IpPrefix IP = IpPrefix.valueOf("127.0.0.1");
57
58 private static final NodeId N1 = new NodeId("node1");
59 private static final NodeId N2 = new NodeId("node2");
60
61 private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
62 private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
63
64 private DistributedMastershipStore dms;
65 private TestDistributedMastershipStore testStore;
66 private KryoSerializationManager serializationMgr;
67 private StoreManager storeMgr;
68
69 @BeforeClass
70 public static void setUpBeforeClass() throws Exception {
71 }
72
73 @AfterClass
74 public static void tearDownAfterClass() throws Exception {
75 }
76
77 @Before
78 public void setUp() throws Exception {
79 // TODO should find a way to clean Hazelcast instance without shutdown.
80 Config config = TestStoreManager.getTestConfig();
81
82 storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
83 storeMgr.activate();
84
85 serializationMgr = new KryoSerializationManager();
86 serializationMgr.activate();
87
88 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
89 dms.clusterService = new TestClusterService();
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -070090 dms.deviceService = new TestDeviceService();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070091 dms.activate();
92
93 testStore = (TestDistributedMastershipStore) dms;
94 }
95
96 @After
97 public void tearDown() throws Exception {
98 dms.deactivate();
99
100 serializationMgr.deactivate();
101
102 storeMgr.deactivate();
103 }
104
105 @Test
106 public void getRole() {
107 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
108 testStore.put(DID1, N1, true, true, true);
109 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
110 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
111 }
112
113 @Test
114 public void getMaster() {
115 assertTrue("wrong store state:", dms.rawMasters.isEmpty());
116
117 testStore.put(DID1, N1, true, false, false);
118 assertEquals("wrong master:", N1, dms.getMaster(DID1));
119 assertNull("wrong master:", dms.getMaster(DID2));
120 }
121
122 @Test
123 public void getDevices() {
124 assertTrue("wrong store state:", dms.rawMasters.isEmpty());
125
126 testStore.put(DID1, N1, true, false, false);
127 testStore.put(DID2, N1, true, false, false);
128 testStore.put(DID3, N2, true, false, false);
129
130 assertEquals("wrong devices",
131 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
132 }
133
134 @Test
135 public void requestRoleAndTerm() {
136 //CN1 is "local"
137 testStore.setCurrent(CN1);
138
139 //if already MASTER, nothing should happen
140 testStore.put(DID2, N1, true, false, false);
141 assertEquals("wrong role for MASTER:", MASTER, dms.requestRole(DID2));
142 assertTrue("wrong state for store:",
143 dms.backups.isEmpty() & dms.rawTerms.isEmpty());
144
145 //populate maps with DID1, N1 thru NONE case
146 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
147 assertTrue("wrong state for store:",
148 !dms.backups.isEmpty() & !dms.rawTerms.isEmpty());
149 assertEquals("wrong term",
150 MastershipTerm.of(N1, 0), dms.getTermFor(DID1));
151
152 //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
153 testStore.setCurrent(CN2);
154 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
155 assertEquals("wrong number of entries:", 2, dms.rawTerms.size());
156
157 //change term and requestRole() again; should persist
158 testStore.increment(DID2);
159 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
160 assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
161 }
162
163 @Test
164 public void setMaster() {
165 //populate maps with DID1, N1 as MASTER thru NONE case
166 testStore.setCurrent(CN1);
167 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
168 assertNull("wrong event:", dms.setMaster(N1, DID1));
169
170 //switch over to N2
171 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
172 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
173
174 //orphan switch - should be rare case
175 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
176 assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
177 //disconnect and reconnect - sign of failing re-election or single-instance channel
178 testStore.reset(true, false, false);
179 dms.setMaster(N2, DID2);
180 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
181 }
182
183 @Test
184 public void unsetMaster() {
185 //populate maps with DID1, N1 as MASTER thru NONE case
186 testStore.setCurrent(CN1);
187 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
188 //no backup, no new MASTER/event
189 assertNull("wrong event:", dms.unsetMaster(N1, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700190
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700191 dms.requestRole(DID1);
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700192 ((TestDeviceService) dms.deviceService).active.add(DID1);
193
194 //add backup CN2, get it elected MASTER by relinquishing
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700195 testStore.setCurrent(CN2);
196 dms.requestRole(DID1);
197 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.unsetMaster(N1, DID1).type());
198 assertEquals("wrong master", N2, dms.getMaster(DID1));
199
200 //STANDBY - nothing here, either
201 assertNull("wrong event:", dms.unsetMaster(N1, DID1));
202 assertEquals("wrong role for node:", STANDBY, dms.getRole(N1, DID1));
203
204 //NONE - nothing happens
205 assertNull("wrong event:", dms.unsetMaster(N1, DID2));
206 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700207
208 //for a device that turned off (not active) - status to NONE
209 ((TestDeviceService) dms.deviceService).active.clear();
210 assertNull("extraneous event:", dms.unsetMaster(N2, DID1));
211 assertEquals("wrong role", NONE, dms.getRole(N2, DID1));
212
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700213 }
214
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700215 @Ignore("Ignore until Delegate spec. is clear.")
216 @Test
217 public void testEvents() throws InterruptedException {
218 //shamelessly copy other distributed store tests
219 final CountDownLatch addLatch = new CountDownLatch(1);
220
221 MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
222 @Override
223 public void notify(MastershipEvent event) {
224 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
225 assertEquals("wrong subject", DID1, event.subject());
226 assertEquals("wrong subject", N1, event.master());
227 addLatch.countDown();
228 }
229 };
230
231 dms.setDelegate(checkAdd);
232 dms.setMaster(N1, DID1);
233 //this will fail until we do something about single-instance-ness
234 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
235 }
236
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700237 private class TestDistributedMastershipStore extends
238 DistributedMastershipStore {
239 public TestDistributedMastershipStore(StoreService storeService,
240 KryoSerializationService kryoSerializationService) {
241 this.storeService = storeService;
242 this.kryoSerializationService = kryoSerializationService;
243 }
244
245 //helper to populate master/backup structures
246 public void put(DeviceId dev, NodeId node,
247 boolean store, boolean backup, boolean term) {
248 if (store) {
249 dms.rawMasters.put(serialize(dev), serialize(node));
250 }
251 if (backup) {
252 dms.backups.put(serialize(node), (byte) 0);
253 }
254 if (term) {
255 dms.rawTerms.put(serialize(dev), 0);
256 }
257 }
258
259 //clears structures
260 public void reset(boolean store, boolean backup, boolean term) {
261 if (store) {
262 dms.rawMasters.clear();
263 }
264 if (backup) {
265 dms.backups.clear();
266 }
267 if (term) {
268 dms.rawTerms.clear();
269 }
270 }
271
272 //increment term for a device
273 public void increment(DeviceId dev) {
274 Integer t = dms.rawTerms.get(serialize(dev));
275 if (t != null) {
276 dms.rawTerms.put(serialize(dev), ++t);
277 }
278 }
279
280 //sets the "local" node
281 public void setCurrent(ControllerNode node) {
282 ((TestClusterService) clusterService).current = node;
283 }
284 }
285
286 private class TestClusterService implements ClusterService {
287
288 protected ControllerNode current;
289
290 @Override
291 public ControllerNode getLocalNode() {
292 return current;
293 }
294
295 @Override
296 public Set<ControllerNode> getNodes() {
297 return Sets.newHashSet(CN1, CN2);
298 }
299
300 @Override
301 public ControllerNode getNode(NodeId nodeId) {
302 return null;
303 }
304
305 @Override
306 public State getState(NodeId nodeId) {
307 return null;
308 }
309
310 @Override
311 public void addListener(ClusterEventListener listener) {
312 }
313
314 @Override
315 public void removeListener(ClusterEventListener listener) {
316 }
317
318 }
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700319
320 private class TestDeviceService implements DeviceService {
321
322 Set<DeviceId> active = Sets.newHashSet();
323
324 @Override
325 public int getDeviceCount() {
326 return 0;
327 }
328
329 @Override
330 public Iterable<Device> getDevices() {
331 return null;
332 }
333
334 @Override
335 public Device getDevice(DeviceId deviceId) {
336 return null;
337 }
338
339 @Override
340 public MastershipRole getRole(DeviceId deviceId) {
341 return null;
342 }
343
344 @Override
345 public List<Port> getPorts(DeviceId deviceId) {
346 return null;
347 }
348
349 @Override
350 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
351 return null;
352 }
353
354 @Override
355 public boolean isAvailable(DeviceId deviceId) {
356 return active.contains(deviceId);
357 }
358
359 @Override
360 public void addListener(DeviceListener listener) {
361 }
362
363 @Override
364 public void removeListener(DeviceListener listener) {
365 }
366
367 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700368}