blob: 7d99259bbcc535058438d793bb0c5f0f7bf9da10 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.mastership.impl;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070017
Ayaka Koshibec4047702014-10-07 14:43:52 -070018import java.util.Map;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070019import java.util.Set;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070020import java.util.concurrent.CountDownLatch;
21import java.util.concurrent.TimeUnit;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070022
23import org.junit.After;
24import org.junit.AfterClass;
25import org.junit.Before;
26import org.junit.BeforeClass;
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -070027import org.junit.Ignore;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070028import org.junit.Test;
Brian O'Connor17367492015-03-03 17:11:54 -080029import org.onlab.junit.TestTools;
Ray Milkeycc53abd2015-02-19 12:31:33 -080030import org.onlab.packet.IpAddress;
31import org.onosproject.cluster.ClusterServiceAdapter;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.cluster.ControllerNode;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.cluster.DefaultControllerNode;
34import org.onosproject.cluster.NodeId;
35import org.onosproject.mastership.MastershipEvent;
Ray Milkeycc53abd2015-02-19 12:31:33 -080036import org.onosproject.mastership.MastershipEvent.Type;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.mastership.MastershipStoreDelegate;
38import org.onosproject.mastership.MastershipTerm;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.DeviceId;
40import org.onosproject.net.MastershipRole;
41import org.onosproject.store.hz.StoreManager;
42import org.onosproject.store.hz.StoreService;
43import org.onosproject.store.hz.TestStoreManager;
44import org.onosproject.store.serializers.KryoSerializer;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070045
46import com.google.common.collect.Sets;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070047
Ray Milkeycc53abd2015-02-19 12:31:33 -080048import static org.junit.Assert.assertEquals;
49import static org.junit.Assert.assertNull;
50import static org.junit.Assert.assertTrue;
51import static org.onosproject.net.MastershipRole.MASTER;
52import static org.onosproject.net.MastershipRole.NONE;
53import static org.onosproject.net.MastershipRole.STANDBY;
54
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070055/**
56 * Test of the Hazelcast-based distributed MastershipStore implementation.
57 */
58public class DistributedMastershipStoreTest {
59
60 private static final DeviceId DID1 = DeviceId.deviceId("of:01");
61 private static final DeviceId DID2 = DeviceId.deviceId("of:02");
62 private static final DeviceId DID3 = DeviceId.deviceId("of:03");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070063
Pavlin Radoslavov444b5192014-10-28 10:45:19 -070064 private static final IpAddress IP = IpAddress.valueOf("127.0.0.1");
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070065
66 private static final NodeId N1 = new NodeId("node1");
67 private static final NodeId N2 = new NodeId("node2");
68
69 private static final ControllerNode CN1 = new DefaultControllerNode(N1, IP);
70 private static final ControllerNode CN2 = new DefaultControllerNode(N2, IP);
71
72 private DistributedMastershipStore dms;
73 private TestDistributedMastershipStore testStore;
Ayaka Koshibe4c891272014-10-08 17:14:16 -070074 private KryoSerializer serializationMgr;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070075 private StoreManager storeMgr;
76
77 @BeforeClass
78 public static void setUpBeforeClass() throws Exception {
79 }
80
81 @AfterClass
82 public static void tearDownAfterClass() throws Exception {
83 }
84
85 @Before
86 public void setUp() throws Exception {
87 // TODO should find a way to clean Hazelcast instance without shutdown.
Yuta HIGUCHI151cad82015-02-04 23:26:50 -080088 TestStoreManager testStoreMgr = new TestStoreManager();
89 testStoreMgr.setHazelcastInstance(testStoreMgr.initSingleInstance());
90 storeMgr = testStoreMgr;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070091 storeMgr.activate();
92
Ayaka Koshibe4c891272014-10-08 17:14:16 -070093 serializationMgr = new KryoSerializer();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070094
95 dms = new TestDistributedMastershipStore(storeMgr, serializationMgr);
96 dms.clusterService = new TestClusterService();
97 dms.activate();
98
99 testStore = (TestDistributedMastershipStore) dms;
100 }
101
102 @After
103 public void tearDown() throws Exception {
104 dms.deactivate();
105
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700106 storeMgr.deactivate();
107 }
108
109 @Test
Ray Milkey0811bdd2015-03-11 10:21:55 -0700110 @Ignore("Disabled this test due to intermittent failures seen on Jenkins runs")
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700111 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);
Brian O'Connor17367492015-03-03 17:11:54 -0800124 TestTools.assertAfter(100, () -> //wait for up to 100ms
125 assertEquals("wrong master:", N1, dms.getMaster(DID1)));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700126 assertNull("wrong master:", dms.getMaster(DID2));
127 }
128
129 @Test
130 public void getDevices() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700131 assertTrue("wrong store state:", dms.roleMap.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700132
133 testStore.put(DID1, N1, true, false, false);
134 testStore.put(DID2, N1, true, false, false);
135 testStore.put(DID3, N2, true, false, false);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700136 assertEquals("wrong devices",
137 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
138 }
139
140 @Test
141 public void requestRoleAndTerm() {
142 //CN1 is "local"
143 testStore.setCurrent(CN1);
144
145 //if already MASTER, nothing should happen
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700146 testStore.put(DID2, N1, true, false, true);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700147 assertEquals("wrong role for MASTER:", MASTER, dms.requestRole(DID2));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700148
149 //populate maps with DID1, N1 thru NONE case
150 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700151 assertTrue("wrong state for store:", !dms.terms.isEmpty());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700152 assertEquals("wrong term",
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700153 MastershipTerm.of(N1, 1), dms.getTermFor(DID1));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700154
155 //CN2 now local. DID2 has N1 as MASTER so N2 is STANDBY
156 testStore.setCurrent(CN2);
157 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
Ayaka Koshibec4047702014-10-07 14:43:52 -0700158 assertEquals("wrong number of entries:", 2, dms.terms.size());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700159
160 //change term and requestRole() again; should persist
161 testStore.increment(DID2);
162 assertEquals("wrong role for STANDBY:", STANDBY, dms.requestRole(DID2));
163 assertEquals("wrong term", MastershipTerm.of(N1, 1), dms.getTermFor(DID2));
164 }
165
166 @Test
167 public void setMaster() {
168 //populate maps with DID1, N1 as MASTER thru NONE case
169 testStore.setCurrent(CN1);
170 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
171 assertNull("wrong event:", dms.setMaster(N1, DID1));
172
173 //switch over to N2
174 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700175 System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700176 assertEquals("wrong term", MastershipTerm.of(N2, 2), dms.getTermFor(DID1));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700177
178 //orphan switch - should be rare case
179 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700180 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700181 //disconnect and reconnect - sign of failing re-election or single-instance channel
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700182 dms.roleMap.clear();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700183 dms.setMaster(N2, DID2);
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700184 assertEquals("wrong term", MastershipTerm.of(N2, 2), dms.getTermFor(DID2));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700185 }
186
187 @Test
Ayaka Koshibec4047702014-10-07 14:43:52 -0700188 public void relinquishRole() {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700189 //populate maps with DID1, N1 as MASTER thru NONE case
190 testStore.setCurrent(CN1);
191 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
192 //no backup, no new MASTER/event
Ayaka Koshibec4047702014-10-07 14:43:52 -0700193 assertNull("wrong event:", dms.relinquishRole(N1, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700194
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700195 dms.requestRole(DID1);
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700196
197 //add backup CN2, get it elected MASTER by relinquishing
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700198 testStore.setCurrent(CN2);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700199 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
200 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type());
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700201 assertEquals("wrong master", N2, dms.getMaster(DID1));
202
Ayaka Koshibec4047702014-10-07 14:43:52 -0700203 //all nodes "give up" on device, which goes back to NONE.
204 assertNull("wrong event:", dms.relinquishRole(N2, DID1));
205 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700206
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700207 assertEquals("wrong number of retired nodes", 2,
208 dms.roleMap.get(DID1).nodesOfRole(NONE).size());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700209
210 //bring nodes back
211 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
212 testStore.setCurrent(CN1);
213 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700214 assertEquals("wrong number of backup nodes", 1,
215 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700216
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700217 //If STANDBY, should drop to NONE
Ayaka Koshibe98bd12f2014-11-01 20:13:37 -0700218 assertEquals("wrong event:", Type.BACKUPS_CHANGED, dms.relinquishRole(N1, DID1).type());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700219 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
220
Ayaka Koshibec4047702014-10-07 14:43:52 -0700221 //NONE - nothing happens
Ayaka Koshibe98bd12f2014-11-01 20:13:37 -0700222 assertEquals("wrong event:", Type.BACKUPS_CHANGED, dms.relinquishRole(N1, DID2).type());
Ayaka Koshibec4047702014-10-07 14:43:52 -0700223 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700224
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700225 }
226
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700227 @Ignore("Ignore until Delegate spec. is clear.")
228 @Test
229 public void testEvents() throws InterruptedException {
230 //shamelessly copy other distributed store tests
231 final CountDownLatch addLatch = new CountDownLatch(1);
232
233 MastershipStoreDelegate checkAdd = new MastershipStoreDelegate() {
234 @Override
235 public void notify(MastershipEvent event) {
236 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
237 assertEquals("wrong subject", DID1, event.subject());
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700238 assertEquals("wrong subject", N1, event.roleInfo().master());
Ayaka Koshibe5c0f2372014-10-02 17:59:04 -0700239 addLatch.countDown();
240 }
241 };
242
243 dms.setDelegate(checkAdd);
244 dms.setMaster(N1, DID1);
245 //this will fail until we do something about single-instance-ness
246 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
247 }
248
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700249 private class TestDistributedMastershipStore extends
250 DistributedMastershipStore {
251 public TestDistributedMastershipStore(StoreService storeService,
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700252 KryoSerializer kryoSerialization) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700253 this.storeService = storeService;
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700254 this.serializer = kryoSerialization;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700255 }
256
257 //helper to populate master/backup structures
258 public void put(DeviceId dev, NodeId node,
Ayaka Koshibec4047702014-10-07 14:43:52 -0700259 boolean master, boolean backup, boolean term) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700260 RoleValue rv = dms.roleMap.get(dev);
261 if (rv == null) {
262 rv = new RoleValue();
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700263 }
Ayaka Koshibec4047702014-10-07 14:43:52 -0700264
265 if (master) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700266 rv.add(MASTER, node);
267 rv.reassign(node, STANDBY, NONE);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700268 }
269 if (backup) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700270 rv.add(STANDBY, node);
271 rv.remove(MASTER, node);
272 rv.remove(NONE, node);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700273 }
274 if (term) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700275 dms.terms.put(dev, 0);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700276 }
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700277 dms.roleMap.put(dev, rv);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700278 }
279
Ayaka Koshibe4c891272014-10-08 17:14:16 -0700280 //a dumb utility function.
Ayaka Koshibec4047702014-10-07 14:43:52 -0700281 public void dump() {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700282 for (Map.Entry<DeviceId, RoleValue> el : dms.roleMap.entrySet()) {
283 System.out.println("DID: " + el.getKey());
284 for (MastershipRole role : MastershipRole.values()) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700285 System.out.println("\t" + role.toString() + ":");
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700286 for (NodeId n : el.getValue().nodesOfRole(role)) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700287 System.out.println("\t\t" + n);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700288 }
289 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700290 }
291 }
292
293 //increment term for a device
294 public void increment(DeviceId dev) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700295 Integer t = dms.terms.get(dev);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700296 if (t != null) {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700297 dms.terms.put(dev, ++t);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700298 }
299 }
300
301 //sets the "local" node
302 public void setCurrent(ControllerNode node) {
303 ((TestClusterService) clusterService).current = node;
304 }
305 }
306
Ray Milkeycc53abd2015-02-19 12:31:33 -0800307 private class TestClusterService extends ClusterServiceAdapter {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700308
309 protected ControllerNode current;
310
311 @Override
312 public ControllerNode getLocalNode() {
313 return current;
314 }
315
316 @Override
317 public Set<ControllerNode> getNodes() {
318 return Sets.newHashSet(CN1, CN2);
319 }
320
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700321 }
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700322
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700323}