blob: b2c0e2ef1b103fd08a6d71e583b7b87fd63d18cc [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
8import java.util.Set;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -07009import java.util.concurrent.CountDownLatch;
10import java.util.concurrent.TimeUnit;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070011
12import org.junit.After;
13import org.junit.AfterClass;
14import org.junit.Before;
15import org.junit.BeforeClass;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070016import org.junit.Ignore;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070017import org.junit.Test;
18import org.onlab.onos.cluster.ClusterEventListener;
19import org.onlab.onos.cluster.ClusterService;
20import org.onlab.onos.cluster.ControllerNode;
21import org.onlab.onos.cluster.ControllerNode.State;
22import org.onlab.onos.cluster.DefaultControllerNode;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070023import org.onlab.onos.cluster.MastershipEvent;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070024import org.onlab.onos.cluster.MastershipEvent.Type;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070025import org.onlab.onos.cluster.MastershipStoreDelegate;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070026import org.onlab.onos.cluster.MastershipTerm;
27import org.onlab.onos.cluster.NodeId;
28import org.onlab.onos.net.DeviceId;
29import org.onlab.onos.store.common.StoreManager;
30import org.onlab.onos.store.common.StoreService;
31import org.onlab.onos.store.common.TestStoreManager;
32import org.onlab.onos.store.serializers.KryoSerializationManager;
33import org.onlab.onos.store.serializers.KryoSerializationService;
34import org.onlab.packet.IpPrefix;
35
36import com.google.common.collect.Sets;
37import com.hazelcast.config.Config;
38import com.hazelcast.core.Hazelcast;
39
40/**
41 * Test of the Hazelcast-based distributed MastershipStore implementation.
42 */
43public class DistributedMastershipStoreTest {
44
45 private static final DeviceId DID1 = DeviceId.deviceId("of:01");
46 private static final DeviceId DID2 = DeviceId.deviceId("of:02");
47 private static final DeviceId DID3 = DeviceId.deviceId("of:03");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070048
49 private static final IpPrefix IP = IpPrefix.valueOf("127.0.0.1");
50
51 private static final NodeId N1 = new NodeId("node1");
52 private static final NodeId N2 = new NodeId("node2");
53
54 private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
55 private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
56
57 private DistributedMastershipStore dms;
58 private TestDistributedMastershipStore testStore;
59 private KryoSerializationManager serializationMgr;
60 private StoreManager storeMgr;
61
62 @BeforeClass
63 public static void setUpBeforeClass() throws Exception {
64 }
65
66 @AfterClass
67 public static void tearDownAfterClass() throws Exception {
68 }
69
70 @Before
71 public void setUp() throws Exception {
72 // TODO should find a way to clean Hazelcast instance without shutdown.
73 Config config = TestStoreManager.getTestConfig();
74
75 storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
76 storeMgr.activate();
77
78 serializationMgr = new KryoSerializationManager();
79 serializationMgr.activate();
80
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
92 serializationMgr.deactivate();
93
94 storeMgr.deactivate();
95 }
96
97 @Test
98 public void getRole() {
99 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
100 testStore.put(DID1, N1, true, true, true);
101 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
102 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
103 }
104
105 @Test
106 public void getMaster() {
107 assertTrue("wrong store state:", dms.rawMasters.isEmpty());
108
109 testStore.put(DID1, N1, true, false, false);
110 assertEquals("wrong master:", N1, dms.getMaster(DID1));
111 assertNull("wrong master:", dms.getMaster(DID2));
112 }
113
114 @Test
115 public void getDevices() {
116 assertTrue("wrong store state:", dms.rawMasters.isEmpty());
117
118 testStore.put(DID1, N1, true, false, false);
119 testStore.put(DID2, N1, true, false, false);
120 testStore.put(DID3, N2, true, false, false);
121
122 assertEquals("wrong devices",
123 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
124 }
125
126 @Test
127 public void requestRoleAndTerm() {
128 //CN1 is "local"
129 testStore.setCurrent(CN1);
130
131 //if already MASTER, nothing should happen
132 testStore.put(DID2, N1, true, false, false);
133 assertEquals("wrong role for MASTER:", MASTER, dms.requestRole(DID2));
134 assertTrue("wrong state for store:",
135 dms.backups.isEmpty() & dms.rawTerms.isEmpty());
136
137 //populate maps with DID1, N1 thru NONE case
138 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
139 assertTrue("wrong state for store:",
140 !dms.backups.isEmpty() & !dms.rawTerms.isEmpty());
141 assertEquals("wrong term",
142 MastershipTerm.of(N1, 0), dms.getTermFor(DID1));
143
144 //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
145 testStore.setCurrent(CN2);
146 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
147 assertEquals("wrong number of entries:", 2, dms.rawTerms.size());
148
149 //change term and requestRole() again; should persist
150 testStore.increment(DID2);
151 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
152 assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
153 }
154
155 @Test
156 public void setMaster() {
157 //populate maps with DID1, N1 as MASTER thru NONE case
158 testStore.setCurrent(CN1);
159 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
160 assertNull("wrong event:", dms.setMaster(N1, DID1));
161
162 //switch over to N2
163 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
164 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
165
166 //orphan switch - should be rare case
167 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
168 assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
169 //disconnect and reconnect - sign of failing re-election or single-instance channel
170 testStore.reset(true, false, false);
171 dms.setMaster(N2, DID2);
172 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
173 }
174
175 @Test
176 public void unsetMaster() {
177 //populate maps with DID1, N1 as MASTER thru NONE case
178 testStore.setCurrent(CN1);
179 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
180 //no backup, no new MASTER/event
181 assertNull("wrong event:", dms.unsetMaster(N1, DID1));
182 //add backup CN2, get it elected MASTER
183 dms.requestRole(DID1);
184 testStore.setCurrent(CN2);
185 dms.requestRole(DID1);
186 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.unsetMaster(N1, DID1).type());
187 assertEquals("wrong master", N2, dms.getMaster(DID1));
188
189 //STANDBY - nothing here, either
190 assertNull("wrong event:", dms.unsetMaster(N1, DID1));
191 assertEquals("wrong role for node:", STANDBY, dms.getRole(N1, DID1));
192
193 //NONE - nothing happens
194 assertNull("wrong event:", dms.unsetMaster(N1, DID2));
195 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
196 }
197
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700198 @Ignore("Ignore until Delegate spec. is clear.")
199 @Test
200 public void testEvents() throws InterruptedException {
201 //shamelessly copy other distributed store tests
202 final CountDownLatch addLatch = new CountDownLatch(1);
203
204 MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
205 @Override
206 public void notify(MastershipEvent event) {
207 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
208 assertEquals("wrong subject", DID1, event.subject());
209 assertEquals("wrong subject", N1, event.master());
210 addLatch.countDown();
211 }
212 };
213
214 dms.setDelegate(checkAdd);
215 dms.setMaster(N1, DID1);
216 //this will fail until we do something about single-instance-ness
217 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
218 }
219
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700220 private class TestDistributedMastershipStore extends
221 DistributedMastershipStore {
222 public TestDistributedMastershipStore(StoreService storeService,
223 KryoSerializationService kryoSerializationService) {
224 this.storeService = storeService;
225 this.kryoSerializationService = kryoSerializationService;
226 }
227
228 //helper to populate master/backup structures
229 public void put(DeviceId dev, NodeId node,
230 boolean store, boolean backup, boolean term) {
231 if (store) {
232 dms.rawMasters.put(serialize(dev), serialize(node));
233 }
234 if (backup) {
235 dms.backups.put(serialize(node), (byte) 0);
236 }
237 if (term) {
238 dms.rawTerms.put(serialize(dev), 0);
239 }
240 }
241
242 //clears structures
243 public void reset(boolean store, boolean backup, boolean term) {
244 if (store) {
245 dms.rawMasters.clear();
246 }
247 if (backup) {
248 dms.backups.clear();
249 }
250 if (term) {
251 dms.rawTerms.clear();
252 }
253 }
254
255 //increment term for a device
256 public void increment(DeviceId dev) {
257 Integer t = dms.rawTerms.get(serialize(dev));
258 if (t != null) {
259 dms.rawTerms.put(serialize(dev), ++t);
260 }
261 }
262
263 //sets the "local" node
264 public void setCurrent(ControllerNode node) {
265 ((TestClusterService) clusterService).current = node;
266 }
267 }
268
269 private class TestClusterService implements ClusterService {
270
271 protected ControllerNode current;
272
273 @Override
274 public ControllerNode getLocalNode() {
275 return current;
276 }
277
278 @Override
279 public Set<ControllerNode> getNodes() {
280 return Sets.newHashSet(CN1, CN2);
281 }
282
283 @Override
284 public ControllerNode getNode(NodeId nodeId) {
285 return null;
286 }
287
288 @Override
289 public State getState(NodeId nodeId) {
290 return null;
291 }
292
293 @Override
294 public void addListener(ClusterEventListener listener) {
295 }
296
297 @Override
298 public void removeListener(ClusterEventListener listener) {
299 }
300
301 }
302}