blob: ed6a859918b7fab4f89cd5eb3642f7fd7c9d0db4 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070016package org.onlab.onos.store.mastership.impl;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070017
18import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.assertNull;
20import static org.junit.Assert.assertTrue;
21import static org.onlab.onos.net.MastershipRole.*;
22
Ayaka Koshibec4047702014-10-07 14:43:52 -070023import java.util.Map;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070024import java.util.Set;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070025import java.util.concurrent.CountDownLatch;
26import java.util.concurrent.TimeUnit;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070027
28import org.junit.After;
29import org.junit.AfterClass;
30import org.junit.Before;
31import org.junit.BeforeClass;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070032import org.junit.Ignore;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070033import org.junit.Test;
34import org.onlab.onos.cluster.ClusterEventListener;
35import org.onlab.onos.cluster.ClusterService;
36import org.onlab.onos.cluster.ControllerNode;
37import org.onlab.onos.cluster.ControllerNode.State;
38import org.onlab.onos.cluster.DefaultControllerNode;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070039import org.onlab.onos.cluster.NodeId;
Yuta HIGUCHI80912e62014-10-12 00:15:47 -070040import org.onlab.onos.mastership.MastershipEvent;
41import org.onlab.onos.mastership.MastershipStoreDelegate;
42import org.onlab.onos.mastership.MastershipTerm;
43import org.onlab.onos.mastership.MastershipEvent.Type;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070044import org.onlab.onos.net.DeviceId;
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070045import org.onlab.onos.net.MastershipRole;
Yuta HIGUCHI41f2ec02014-10-27 09:54:43 -070046import org.onlab.onos.store.hz.StoreManager;
47import org.onlab.onos.store.hz.StoreService;
48import org.onlab.onos.store.hz.TestStoreManager;
Ayaka Koshibe4c891272014-10-08 17:14:16 -070049import org.onlab.onos.store.serializers.KryoSerializer;
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070050import org.onlab.packet.IpAddress;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070051
52import com.google.common.collect.Sets;
53import com.hazelcast.config.Config;
54import com.hazelcast.core.Hazelcast;
55
56/**
57 * Test of the Hazelcast-based distributed MastershipStore implementation.
58 */
59public class DistributedMastershipStoreTest {
60
61 private static final DeviceId DID1 = DeviceId.deviceId("of:01");
62 private static final DeviceId DID2 = DeviceId.deviceId("of:02");
63 private static final DeviceId DID3 = DeviceId.deviceId("of:03");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070064
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070065 private static final IpAddress IP = IpAddress.valueOf("127.0.0.1");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070066
67 private static final NodeId N1 = new NodeId("node1");
68 private static final NodeId N2 = new NodeId("node2");
69
70 private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
71 private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
72
73 private DistributedMastershipStore dms;
74 private TestDistributedMastershipStore testStore;
Ayaka Koshibe4c891272014-10-08 17:14:16 -070075 private KryoSerializer serializationMgr;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070076 private StoreManager storeMgr;
77
78 @BeforeClass
79 public static void setUpBeforeClass() throws Exception {
80 }
81
82 @AfterClass
83 public static void tearDownAfterClass() throws Exception {
84 }
85
86 @Before
87 public void setUp() throws Exception {
88 // TODO should find a way to clean Hazelcast instance without shutdown.
89 Config config = TestStoreManager.getTestConfig();
90
91 storeMgr = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
92 storeMgr.activate();
93
Ayaka Koshibe4c891272014-10-08 17:14:16 -070094 serializationMgr = new KryoSerializer();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070095
96 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
97 dms.clusterService = new TestClusterService();
98 dms.activate();
99
100 testStore = (TestDistributedMastershipStore) dms;
101 }
102
103 @After
104 public void tearDown() throws Exception {
105 dms.deactivate();
106
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700107 storeMgr.deactivate();
108 }
109
110 @Test
111 public void getRole() {
112 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700113 testStore.put(DID1, N1, true, false, true);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700114 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700115 testStore.put(DID1, N2, false, true, false);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700116 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
117 }
118
119 @Test
120 public void getMaster() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700121 assertTrue("wrong store state:", dms.roleMap.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700122
123 testStore.put(DID1, N1, true, false, false);
124 assertEquals("wrong master:", N1, dms.getMaster(DID1));
125 assertNull("wrong master:", dms.getMaster(DID2));
126 }
127
128 @Test
129 public void getDevices() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700130 assertTrue("wrong store state:", dms.roleMap.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700131
132 testStore.put(DID1, N1, true, false, false);
133 testStore.put(DID2, N1, true, false, false);
134 testStore.put(DID3, N2, true, false, false);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700135 assertEquals("wrong devices",
136 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
137 }
138
139 @Test
140 public void requestRoleAndTerm() {
141 //CN1 is "local"
142 testStore.setCurrent(CN1);
143
144 //if already MASTER, nothing should happen
145 testStore.put(DID2, N1, true, false, false);
146 assertEquals("wrong role for MASTER:", MASTER, dms.requestRole(DID2));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700147
148 //populate maps with DID1, N1 thru NONE case
149 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700150 assertTrue("wrong state for store:", !dms.terms.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700151 assertEquals("wrong term",
152 MastershipTerm.of(N1, 0), dms.getTermFor(DID1));
153
154 //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
155 testStore.setCurrent(CN2);
156 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700157 assertEquals("wrong number of entries:", 2, dms.terms.size());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700158
159 //change term and requestRole() again; should persist
160 testStore.increment(DID2);
161 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
162 assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
163 }
164
165 @Test
166 public void setMaster() {
167 //populate maps with DID1, N1 as MASTER thru NONE case
168 testStore.setCurrent(CN1);
169 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
170 assertNull("wrong event:", dms.setMaster(N1, DID1));
171
172 //switch over to N2
173 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700174 System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700175 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
176
177 //orphan switch - should be rare case
178 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
179 assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
180 //disconnect and reconnect - sign of failing re-election or single-instance channel
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700181 dms.roleMap.clear();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700182 dms.setMaster(N2, DID2);
183 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
184 }
185
186 @Test
Ayaka Koshibec4047702014-10-07 14:43:52 -0700187 public void relinquishRole() {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700188 //populate maps with DID1, N1 as MASTER thru NONE case
189 testStore.setCurrent(CN1);
190 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
191 //no backup, no new MASTER/event
Ayaka Koshibec4047702014-10-07 14:43:52 -0700192 assertNull("wrong event:", dms.relinquishRole(N1, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700193
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700194 dms.requestRole(DID1);
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700195
196 //add backup CN2, get it elected MASTER by relinquishing
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700197 testStore.setCurrent(CN2);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700198 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
199 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700200 assertEquals("wrong master", N2, dms.getMaster(DID1));
201
Ayaka Koshibec4047702014-10-07 14:43:52 -0700202 //all nodes "give up" on device, which goes back to NONE.
203 assertNull("wrong event:", dms.relinquishRole(N2, DID1));
204 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700205
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700206 assertEquals("wrong number of retired nodes", 2,
207 dms.roleMap.get(DID1).nodesOfRole(NONE).size());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700208
209 //bring nodes back
210 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
211 testStore.setCurrent(CN1);
212 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700213 assertEquals("wrong number of backup nodes", 1,
214 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700215
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700216 //If STANDBY, should drop to NONE
217 assertNull("wrong event:", dms.relinquishRole(N1, DID1));
218 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
219
Ayaka Koshibec4047702014-10-07 14:43:52 -0700220 //NONE - nothing happens
221 assertNull("wrong event:", dms.relinquishRole(N1, DID2));
222 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700223
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700224 }
225
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700226 @Ignore("Ignore until Delegate spec. is clear.")
227 @Test
228 public void testEvents() throws InterruptedException {
229 //shamelessly copy other distributed store tests
230 final CountDownLatch addLatch = new CountDownLatch(1);
231
232 MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
233 @Override
234 public void notify(MastershipEvent event) {
235 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
236 assertEquals("wrong subject", DID1, event.subject());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700237 assertEquals("wrong subject", N1, event.roleInfo().master());
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700238 addLatch.countDown();
239 }
240 };
241
242 dms.setDelegate(checkAdd);
243 dms.setMaster(N1, DID1);
244 //this will fail until we do something about single-instance-ness
245 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
246 }
247
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700248 private class TestDistributedMastershipStore extends
249 DistributedMastershipStore {
250 public TestDistributedMastershipStore(StoreService storeService,
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700251 KryoSerializer kryoSerialization) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700252 this.storeService = storeService;
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700253 this.serializer = kryoSerialization;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700254 }
255
256 //helper to populate master/backup structures
257 public void put(DeviceId dev, NodeId node,
Ayaka Koshibec4047702014-10-07 14:43:52 -0700258 boolean master, boolean backup, boolean term) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700259 RoleValue rv = dms.roleMap.get(dev);
260 if (rv == null) {
261 rv = new RoleValue();
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700262 }
Ayaka Koshibec4047702014-10-07 14:43:52 -0700263
264 if (master) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700265 rv.add(MASTER, node);
266 rv.reassign(node, STANDBY, NONE);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700267 }
268 if (backup) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700269 rv.add(STANDBY, node);
270 rv.remove(MASTER, node);
271 rv.remove(NONE, node);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700272 }
273 if (term) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700274 dms.terms.put(dev, 0);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700275 }
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700276 dms.roleMap.put(dev, rv);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700277 }
278
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700279 //a dumb utility function.
Ayaka Koshibec4047702014-10-07 14:43:52 -0700280 public void dump() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700281 for (Map.Entry<DeviceId, RoleValue> el : dms.roleMap.entrySet()) {
282 System.out.println("DID: " + el.getKey());
283 for (MastershipRole role : MastershipRole.values()) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700284 System.out.println("\t" + role.toString() + ":");
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700285 for (NodeId n : el.getValue().nodesOfRole(role)) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700286 System.out.println("\t\t" + n);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700287 }
288 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700289 }
290 }
291
292 //increment term for a device
293 public void increment(DeviceId dev) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700294 Integer t = dms.terms.get(dev);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700295 if (t != null) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700296 dms.terms.put(dev, ++t);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700297 }
298 }
299
300 //sets the "local" node
301 public void setCurrent(ControllerNode node) {
302 ((TestClusterService) clusterService).current = node;
303 }
304 }
305
306 private class TestClusterService implements ClusterService {
307
308 protected ControllerNode current;
309
310 @Override
311 public ControllerNode getLocalNode() {
312 return current;
313 }
314
315 @Override
316 public Set<ControllerNode> getNodes() {
317 return Sets.newHashSet(CN1, CN2);
318 }
319
320 @Override
321 public ControllerNode getNode(NodeId nodeId) {
322 return null;
323 }
324
325 @Override
326 public State getState(NodeId nodeId) {
327 return null;
328 }
329
330 @Override
331 public void addListener(ClusterEventListener listener) {
332 }
333
334 @Override
335 public void removeListener(ClusterEventListener listener) {
336 }
337
338 }
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700339
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700340}