2 * Copyright 2014-2015 Open Networking Laboratory
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.onosproject.store.mastership.impl;
19 * Test of the Hazelcast-based distributed MastershipStore implementation.
21 public class DistributedMastershipStoreTest {
23 private static final DeviceId DID1 = DeviceId.deviceId("of:01");
24 private static final DeviceId DID2 = DeviceId.deviceId("of:02");
25 private static final DeviceId DID3 = DeviceId.deviceId("of:03");
27 private static final IpAddress IP = IpAddress.valueOf("127.0.0.1");
29 private static final NodeId N1 = new NodeId("node1");
30 private static final NodeId N2 = new NodeId("node2");
32 private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
33 private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
35 private DistributedMastershipStore dms;
36 private TestDistributedMastershipStore testStore;
37 private KryoSerializer serializationMgr;
38 private StoreManager storeMgr;
41 public static void setUpBeforeClass() throws Exception {
45 public static void tearDownAfterClass() throws Exception {
49 public void setUp() throws Exception {
50 // TODO should find a way to clean Hazelcast instance without shutdown.
51 TestStoreManager testStoreMgr = new TestStoreManager();
52 testStoreMgr.setHazelcastInstance(testStoreMgr.initSingleInstance());
53 storeMgr = testStoreMgr;
56 serializationMgr = new KryoSerializer();
58 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
59 dms.clusterService = new TestClusterService();
62 testStore = (TestDistributedMastershipStore) dms;
66 public void tearDown() throws Exception {
69 storeMgr.deactivate();
73 @Ignore("Disabled this test due to intermittent failures seen on Jenkins runs")
74 public void getRole() {
75 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
76 testStore.put(DID1, N1, true, false, true);
77 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
78 testStore.put(DID1, N2, false, true, false);
79 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
83 public void getMaster() {
84 assertTrue("wrong store state:", dms.roleMap.isEmpty());
86 testStore.put(DID1, N1, true, false, false);
87 TestTools.assertAfter(100, () -> //wait for up to 100ms
88 assertEquals("wrong master:", N1, dms.getMaster(DID1)));
89 assertNull("wrong master:", dms.getMaster(DID2));
93 public void getDevices() {
94 assertTrue("wrong store state:", dms.roleMap.isEmpty());
96 testStore.put(DID1, N1, true, false, false);
97 testStore.put(DID2, N1, true, false, false);
98 testStore.put(DID3, N2, true, false, false);
99 assertEquals("wrong devices",
100 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
104 public void requestRoleAndTerm() {
106 testStore.setCurrent(CN1);
108 //if already MASTER, nothing should happen
109 testStore.put(DID2, N1, true, false, true);
110 assertEquals("wrong role for MASTER:", MASTER, Futures.getUnchecked(dms.requestRole(DID2)));
112 //populate maps with DID1, N1 thru NONE case
113 assertEquals("wrong role for NONE:", MASTER, Futures.getUnchecked(dms.requestRole(DID1)));
114 assertTrue("wrong state for store:", !dms.terms.isEmpty());
115 assertEquals("wrong term",
116 MastershipTerm.of(N1, 1), dms.getTermFor(DID1));
118 //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
119 testStore.setCurrent(CN2);
120 assertEquals("wrong role for STANDBY:", STANDBY, Futures.getUnchecked(dms.requestRole(DID2)));
121 assertEquals("wrong number of entries:", 2, dms.terms.size());
123 //change term and requestRole() again; should persist
124 testStore.increment(DID2);
125 assertEquals("wrong role for STANDBY:", STANDBY, Futures.getUnchecked(dms.requestRole(DID2)));
126 assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
130 public void setMaster() {
131 //populate maps with DID1, N1 as MASTER thru NONE case
132 testStore.setCurrent(CN1);
133 assertEquals("wrong role for NONE:", MASTER, Futures.getUnchecked(dms.requestRole(DID1)));
134 assertNull("wrong event:", Futures.getUnchecked(dms.setMaster(N1, DID1)));
137 assertEquals("wrong event:", Type.MASTER_CHANGED, Futures.getUnchecked(dms.setMaster(N2, DID1)).type());
138 System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
139 assertEquals("wrong term", MastershipTerm.of(N2, 2), dms.getTermFor(DID1));
141 //orphan switch - should be rare case
142 assertEquals("wrong event:", Type.MASTER_CHANGED, Futures.getUnchecked(dms.setMaster(N2, DID2)).type());
143 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
144 //disconnect and reconnect - sign of failing re-election or single-instance channel
146 dms.setMaster(N2, DID2);
147 assertEquals("wrong term", MastershipTerm.of(N2, 2), dms.getTermFor(DID2));
151 public void relinquishRole() {
152 //populate maps with DID1, N1 as MASTER thru NONE case
153 testStore.setCurrent(CN1);
154 assertEquals("wrong role for NONE:", MASTER, Futures.getUnchecked(dms.requestRole(DID1)));
155 //no backup, no new MASTER/event
156 assertNull("wrong event:", Futures.getUnchecked(dms.relinquishRole(N1, DID1)));
158 dms.requestRole(DID1);
160 //add backup CN2, get it elected MASTER by relinquishing
161 testStore.setCurrent(CN2);
162 assertEquals("wrong role for NONE:", STANDBY, Futures.getUnchecked(dms.requestRole(DID1)));
163 assertEquals("wrong event:", Type.MASTER_CHANGED, Futures.getUnchecked(dms.relinquishRole(N1, DID1)).type());
164 assertEquals("wrong master", N2, dms.getMaster(DID1));
166 //all nodes "give up" on device, which goes back to NONE.
167 assertNull("wrong event:", Futures.getUnchecked(dms.relinquishRole(N2, DID1)));
168 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
170 assertEquals("wrong number of retired nodes", 2,
171 dms.roleMap.get(DID1).nodesOfRole(NONE).size());
174 assertEquals("wrong role for NONE:", MASTER, Futures.getUnchecked(dms.requestRole(DID1)));
175 testStore.setCurrent(CN1);
176 assertEquals("wrong role for NONE:", STANDBY, Futures.getUnchecked(dms.requestRole(DID1)));
177 assertEquals("wrong number of backup nodes", 1,
178 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
180 //If STANDBY, should drop to NONE
181 assertEquals("wrong event:", Type.BACKUPS_CHANGED, Futures.getUnchecked(dms.relinquishRole(N1, DID1)).type());
182 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
184 //NONE - nothing happens
185 assertEquals("wrong event:", Type.BACKUPS_CHANGED, Futures.getUnchecked(dms.relinquishRole(N1, DID2)).type());
186 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
190 @Ignore("Ignore until Delegate spec. is clear.")
192 public void testEvents() throws InterruptedException {
193 //shamelessly copy other distributed store tests
194 final CountDownLatch addLatch = new CountDownLatch(1);
196 MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
198 public void notify(MastershipEvent event) {
199 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
200 assertEquals("wrong subject", DID1, event.subject());
201 assertEquals("wrong subject", N1, event.roleInfo().master());
202 addLatch.countDown();
206 dms.setDelegate(checkAdd);
207 dms.setMaster(N1, DID1);
208 //this will fail until we do something about single-instance-ness
209 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
212 private class TestDistributedMastershipStore extends
213 DistributedMastershipStore {
214 public TestDistributedMastershipStore(StoreService storeService,
215 KryoSerializer kryoSerialization) {
216 this.storeService = storeService;
217 this.serializer = kryoSerialization;
220 //helper to populate master/backup structures
221 public void put(DeviceId dev, NodeId node,
222 boolean master, boolean backup, boolean term) {
223 RoleValue rv = dms.roleMap.get(dev);
225 rv = new RoleValue();
229 rv.add(MASTER, node);
230 rv.reassign(node, STANDBY, NONE);
233 rv.add(STANDBY, node);
234 rv.remove(MASTER, node);
235 rv.remove(NONE, node);
238 dms.terms.put(dev, 0);
240 dms.roleMap.put(dev, rv);
243 //a dumb utility function.
245 for (Map.Entry<DeviceId, RoleValue> el : dms.roleMap.entrySet()) {
246 System.out.println("DID: " + el.getKey());
247 for (MastershipRole role : MastershipRole.values()) {
248 System.out.println("\t" + role.toString() + ":");
249 for (NodeId n : el.getValue().nodesOfRole(role)) {
250 System.out.println("\t\t" + n);
256 //increment term for a device
257 public void increment(DeviceId dev) {
258 Integer t = dms.terms.get(dev);
260 dms.terms.put(dev, ++t);
264 //sets the "local" node
265 public void setCurrent(ControllerNode node) {
266 ((TestClusterService) clusterService).current = node;
270 private class TestClusterService extends ClusterServiceAdapter {
272 protected ControllerNode current;
275 public ControllerNode getLocalNode() {
280 public Set<ControllerNode> getNodes() {
281 return Sets.newHashSet(CN1, CN2);