blob: 17651f138c751221356074e3bd253d28c9fa3725 [file] [log] [blame]
Jonathan Hart7061acd2015-03-04 13:15:32 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Jonathan Hart7061acd2015-03-04 13:15:32 -08003 *
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 */
16package org.onosproject.store.intent.impl;
17
18import org.junit.Before;
19import org.junit.Test;
20import org.onlab.junit.NullScheduledExecutor;
21import org.onlab.packet.IpAddress;
22import org.onosproject.cluster.ClusterServiceAdapter;
23import org.onosproject.cluster.ControllerNode;
24import org.onosproject.cluster.DefaultControllerNode;
Madan Jampani620f70d2016-01-30 22:22:47 -080025import org.onosproject.cluster.Leader;
Jonathan Hart7061acd2015-03-04 13:15:32 -080026import org.onosproject.cluster.Leadership;
27import org.onosproject.cluster.LeadershipEvent;
28import org.onosproject.cluster.LeadershipEventListener;
29import org.onosproject.cluster.LeadershipService;
30import org.onosproject.cluster.LeadershipServiceAdapter;
31import org.onosproject.cluster.NodeId;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070032import org.onosproject.common.event.impl.TestEventDispatcher;
Jonathan Hart7061acd2015-03-04 13:15:32 -080033import org.onosproject.net.intent.Key;
34
Madan Jampani620f70d2016-01-30 22:22:47 -080035import java.util.Arrays;
Jonathan Hart7061acd2015-03-04 13:15:32 -080036import java.util.HashSet;
Madan Jampania4a59942016-05-02 11:25:34 -070037import java.util.List;
Jonathan Hart7061acd2015-03-04 13:15:32 -080038import java.util.Objects;
39import java.util.Set;
Madan Jampania4a59942016-05-02 11:25:34 -070040
Jonathan Hart7061acd2015-03-04 13:15:32 -080041import static junit.framework.TestCase.assertFalse;
42import static org.easymock.EasyMock.anyObject;
43import static org.easymock.EasyMock.anyString;
44import static org.easymock.EasyMock.createMock;
45import static org.easymock.EasyMock.expect;
46import static org.easymock.EasyMock.expectLastCall;
47import static org.easymock.EasyMock.replay;
48import static org.easymock.EasyMock.reset;
49import static org.easymock.EasyMock.verify;
50import static org.junit.Assert.assertTrue;
51
52/**
Madan Jampani3b8101a2016-09-15 13:22:01 -070053 * Unit tests for the WorkPartitionManager class.
Jonathan Hart7061acd2015-03-04 13:15:32 -080054 */
Madan Jampani3b8101a2016-09-15 13:22:01 -070055public class WorkPartitionManagerTest {
Jonathan Hart7061acd2015-03-04 13:15:32 -080056
57 private final LeadershipEvent event
Madan Jampani620f70d2016-01-30 22:22:47 -080058 = new LeadershipEvent(LeadershipEvent.Type.CANDIDATES_CHANGED,
Jonathan Hart7061acd2015-03-04 13:15:32 -080059 new Leadership(ELECTION_PREFIX + "0",
Madan Jampani620f70d2016-01-30 22:22:47 -080060 new Leader(MY_NODE_ID, 0, 0),
61 Arrays.asList(MY_NODE_ID, OTHER_NODE_ID)));
Jonathan Hart7061acd2015-03-04 13:15:32 -080062
63 private static final NodeId MY_NODE_ID = new NodeId("local");
64 private static final NodeId OTHER_NODE_ID = new NodeId("other");
65 private static final NodeId INACTIVE_NODE_ID = new NodeId("inactive");
66
Madan Jampani3b8101a2016-09-15 13:22:01 -070067 private static final String ELECTION_PREFIX = "work-partition-";
Jonathan Hart7061acd2015-03-04 13:15:32 -080068
69 private LeadershipService leadershipService;
70 private LeadershipEventListener leaderListener;
71
Madan Jampani3b8101a2016-09-15 13:22:01 -070072 private WorkPartitionManager partitionManager;
Jonathan Hart7061acd2015-03-04 13:15:32 -080073
74 @Before
75 public void setUp() {
76 leadershipService = createMock(LeadershipService.class);
77
78 leadershipService.addListener(anyObject(LeadershipEventListener.class));
79 expectLastCall().andDelegateTo(new TestLeadershipService());
Madan Jampani3b8101a2016-09-15 13:22:01 -070080 for (int i = 0; i < WorkPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampanide003d92015-05-11 17:14:20 -070081 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
Madan Jampani620f70d2016-01-30 22:22:47 -080082 .andReturn(null)
Madan Jampanide003d92015-05-11 17:14:20 -070083 .times(1);
84 }
Jonathan Hart7061acd2015-03-04 13:15:32 -080085
Madan Jampani3b8101a2016-09-15 13:22:01 -070086 partitionManager = new WorkPartitionManager()
Jonathan Hart7061acd2015-03-04 13:15:32 -080087 .withScheduledExecutor(new NullScheduledExecutor());
88
89 partitionManager.clusterService = new TestClusterService();
Madan Jampania9673fd2016-02-02 13:01:29 -080090 partitionManager.localNodeId = MY_NODE_ID;
Jonathan Hart7061acd2015-03-04 13:15:32 -080091 partitionManager.leadershipService = leadershipService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070092 partitionManager.eventDispatcher = new TestEventDispatcher();
Jonathan Hart7061acd2015-03-04 13:15:32 -080093 }
94
95 /**
96 * Configures a mock leadership service to have the specified number of
97 * partitions owned by the local node and all other partitions owned by a
98 * (fake) remote node.
99 *
100 * @param numMine number of partitions that should be owned by the local node
101 */
102 private void setUpLeadershipService(int numMine) {
Madan Jampania4a59942016-05-02 11:25:34 -0700103 List<NodeId> allNodes = Arrays.asList(MY_NODE_ID, OTHER_NODE_ID);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800104 for (int i = 0; i < numMine; i++) {
Madan Jampania4a59942016-05-02 11:25:34 -0700105 expect(leadershipService.getLeadership(ELECTION_PREFIX + i))
106 .andReturn(new Leadership(ELECTION_PREFIX + i,
107 new Leader(MY_NODE_ID, 1, 1000),
108 allNodes))
109 .anyTimes();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800110 }
111
Madan Jampani3b8101a2016-09-15 13:22:01 -0700112 for (int i = numMine; i < WorkPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampania4a59942016-05-02 11:25:34 -0700113 expect(leadershipService.getLeadership(ELECTION_PREFIX + i))
114 .andReturn(new Leadership(ELECTION_PREFIX + i,
115 new Leader(OTHER_NODE_ID, 1, 1000),
116 allNodes))
117 .anyTimes();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800118 }
Madan Jampani3b8101a2016-09-15 13:22:01 -0700119 for (int i = 0; i < WorkPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampani783d3d22016-06-13 17:40:02 -0700120 expect(leadershipService.getCandidates(ELECTION_PREFIX + i))
121 .andReturn(Arrays.asList(MY_NODE_ID, OTHER_NODE_ID))
122 .anyTimes();
123 }
Jonathan Hart7061acd2015-03-04 13:15:32 -0800124 }
125
126 /**
127 * Tests that the PartitionManager's activate method correctly runs for
128 * all the leader elections that it should.
129 */
130 @Test
131 public void testActivate() {
132 reset(leadershipService);
133
134 leadershipService.addListener(anyObject(LeadershipEventListener.class));
135
Madan Jampani3b8101a2016-09-15 13:22:01 -0700136 for (int i = 0; i < WorkPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampanide003d92015-05-11 17:14:20 -0700137 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
Madan Jampani620f70d2016-01-30 22:22:47 -0800138 .andReturn(null)
Madan Jampanide003d92015-05-11 17:14:20 -0700139 .times(1);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800140 }
141
142 replay(leadershipService);
143
144 partitionManager.activate();
145
146 verify(leadershipService);
147 }
148
149 /**
150 * Tests that the isMine method returns the correct result based on the
151 * underlying leadership service data.
152 */
153 @Test
154 public void testIsMine() {
155 // We'll own only the first partition
156 setUpLeadershipService(1);
157 replay(leadershipService);
158
159 Key myKey = new ControllableHashKey(0);
160 Key notMyKey = new ControllableHashKey(1);
161
Madan Jampani3b8101a2016-09-15 13:22:01 -0700162 assertTrue(partitionManager.isMine(myKey, Key::hash));
163 assertFalse(partitionManager.isMine(notMyKey, Key::hash));
Jonathan Hart7061acd2015-03-04 13:15:32 -0800164
165 // Make us the owner of 4 partitions now
166 reset(leadershipService);
167 setUpLeadershipService(4);
168 replay(leadershipService);
169
Madan Jampani3b8101a2016-09-15 13:22:01 -0700170 assertTrue(partitionManager.isMine(myKey, Key::hash));
Jonathan Hart7061acd2015-03-04 13:15:32 -0800171 // notMyKey is now my key because because we're in control of that
172 // partition now
Madan Jampani3b8101a2016-09-15 13:22:01 -0700173 assertTrue(partitionManager.isMine(notMyKey, Key::hash));
Jonathan Hart7061acd2015-03-04 13:15:32 -0800174
Madan Jampani3b8101a2016-09-15 13:22:01 -0700175 assertFalse(partitionManager.isMine(new ControllableHashKey(4), Key::hash));
Jonathan Hart7061acd2015-03-04 13:15:32 -0800176 }
177
178 /**
179 * Tests sending in LeadershipServiceEvents in the case when we have
180 * too many partitions. The event will trigger the partition manager to
Madan Jampani4732c1b2015-05-19 17:11:50 -0700181 * schedule a rebalancing activity.
Jonathan Hart7061acd2015-03-04 13:15:32 -0800182 */
183 @Test
Madan Jampani4732c1b2015-05-19 17:11:50 -0700184 public void testRebalanceScheduling() {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800185 // We have all the partitions so we'll need to relinquish some
Madan Jampani3b8101a2016-09-15 13:22:01 -0700186 setUpLeadershipService(WorkPartitionManager.NUM_PARTITIONS);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800187
Jonathan Hart7061acd2015-03-04 13:15:32 -0800188 replay(leadershipService);
189
190 partitionManager.activate();
191 // Send in the event
192 leaderListener.event(event);
193
Madan Jampani4732c1b2015-05-19 17:11:50 -0700194 assertTrue(partitionManager.rebalanceScheduled.get());
195
Jonathan Hart7061acd2015-03-04 13:15:32 -0800196 verify(leadershipService);
197 }
198
199 /**
Madan Jampani4732c1b2015-05-19 17:11:50 -0700200 * Tests rebalance will trigger the right now of leadership withdraw calls.
Jonathan Hart7061acd2015-03-04 13:15:32 -0800201 */
202 @Test
Madan Jampani4732c1b2015-05-19 17:11:50 -0700203 public void testRebalance() {
204 // We have all the partitions so we'll need to relinquish some
Madan Jampani3b8101a2016-09-15 13:22:01 -0700205 setUpLeadershipService(WorkPartitionManager.NUM_PARTITIONS);
Madan Jampani4732c1b2015-05-19 17:11:50 -0700206
Madan Jampani620f70d2016-01-30 22:22:47 -0800207 leadershipService.withdraw(anyString());
208 expectLastCall().times(7);
Madan Jampani4732c1b2015-05-19 17:11:50 -0700209
210 replay(leadershipService);
211
212 partitionManager.activate();
213
214 // trigger rebalance
215 partitionManager.doRebalance();
216
217 verify(leadershipService);
218 }
219
220 /**
221 * Tests that attempts to rebalance when the paritions are already
222 * evenly distributed does not result in any relinquish attempts.
223 */
224 @Test
225 public void testNoRebalance() {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800226 // Partitions are already perfectly balanced among the two active instances
Madan Jampani3b8101a2016-09-15 13:22:01 -0700227 setUpLeadershipService(WorkPartitionManager.NUM_PARTITIONS / 2);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800228 replay(leadershipService);
229
230 partitionManager.activate();
231
Madan Jampani4732c1b2015-05-19 17:11:50 -0700232 // trigger rebalance
233 partitionManager.doRebalance();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800234
235 verify(leadershipService);
236
237 reset(leadershipService);
238 // We have a smaller share than we should
Madan Jampani3b8101a2016-09-15 13:22:01 -0700239 setUpLeadershipService(WorkPartitionManager.NUM_PARTITIONS / 2 - 1);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800240 replay(leadershipService);
241
Madan Jampani4732c1b2015-05-19 17:11:50 -0700242 // trigger rebalance
243 partitionManager.doRebalance();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800244
245 verify(leadershipService);
246 }
247
248 /**
249 * LeadershipService that allows us to grab a reference to
250 * PartitionManager's LeadershipEventListener.
251 */
252 public class TestLeadershipService extends LeadershipServiceAdapter {
253 @Override
254 public void addListener(LeadershipEventListener listener) {
255 leaderListener = listener;
256 }
257 }
258
259 /**
260 * ClusterService set up with a very simple cluster - 3 nodes, one is the
261 * current node, one is a different active node, and one is an inactive node.
262 */
263 private class TestClusterService extends ClusterServiceAdapter {
264
265 private final ControllerNode self =
266 new DefaultControllerNode(MY_NODE_ID, IpAddress.valueOf(1));
267 private final ControllerNode otherNode =
268 new DefaultControllerNode(OTHER_NODE_ID, IpAddress.valueOf(2));
269 private final ControllerNode inactiveNode =
270 new DefaultControllerNode(INACTIVE_NODE_ID, IpAddress.valueOf(3));
271
272 Set<ControllerNode> nodes;
273
274 public TestClusterService() {
275 nodes = new HashSet<>();
276 nodes.add(self);
277 nodes.add(otherNode);
278 nodes.add(inactiveNode);
279 }
280
281 @Override
282 public ControllerNode getLocalNode() {
283 return self;
284 }
285
286 @Override
287 public Set<ControllerNode> getNodes() {
288 return nodes;
289 }
290
291 @Override
292 public ControllerNode getNode(NodeId nodeId) {
293 return nodes.stream()
294 .filter(c -> c.id().equals(nodeId))
295 .findFirst()
296 .get();
297 }
298
299 @Override
300 public ControllerNode.State getState(NodeId nodeId) {
301 return nodeId.equals(INACTIVE_NODE_ID) ? ControllerNode.State.INACTIVE :
302 ControllerNode.State.ACTIVE;
303 }
304 }
305
306 /**
307 * A key that always hashes to a value provided to the constructor. This
308 * allows us to control the hash of the key for unit tests.
309 */
310 private class ControllableHashKey extends Key {
311
312 protected ControllableHashKey(long hash) {
313 super(hash);
314 }
315
316 @Override
317 public int hashCode() {
318 return Objects.hash(hash());
319 }
320
321 @Override
322 public boolean equals(Object obj) {
323 if (!(obj instanceof ControllableHashKey)) {
324 return false;
325 }
326
327 ControllableHashKey that = (ControllableHashKey) obj;
328
329 return Objects.equals(this.hash(), that.hash());
330 }
Simon Hunte2b6a2b2015-10-27 11:38:51 -0700331
332 @Override
333 public int compareTo(Key o) {
334 Long thisHash = hash();
335 return thisHash.compareTo(o.hash());
336 }
Jonathan Hart7061acd2015-03-04 13:15:32 -0800337 }
338}