2 * Copyright 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.intent.impl;
18 import org.junit.Before;
19 import org.junit.Test;
20 import org.onlab.junit.NullScheduledExecutor;
21 import org.onlab.packet.IpAddress;
22 import org.onosproject.cluster.ClusterServiceAdapter;
23 import org.onosproject.cluster.ControllerNode;
24 import org.onosproject.cluster.DefaultControllerNode;
25 import org.onosproject.cluster.Leadership;
26 import org.onosproject.cluster.LeadershipEvent;
27 import org.onosproject.cluster.LeadershipEventListener;
28 import org.onosproject.cluster.LeadershipService;
29 import org.onosproject.cluster.LeadershipServiceAdapter;
30 import org.onosproject.cluster.NodeId;
31 import org.onosproject.common.event.impl.TestEventDispatcher;
32 import org.onosproject.net.intent.Key;
34 import java.util.HashMap;
35 import java.util.HashSet;
37 import java.util.Objects;
39 import java.util.concurrent.CompletableFuture;
41 import static junit.framework.TestCase.assertFalse;
42 import static org.easymock.EasyMock.anyObject;
43 import static org.easymock.EasyMock.anyString;
44 import static org.easymock.EasyMock.createMock;
45 import static org.easymock.EasyMock.expect;
46 import static org.easymock.EasyMock.expectLastCall;
47 import static org.easymock.EasyMock.replay;
48 import static org.easymock.EasyMock.reset;
49 import static org.easymock.EasyMock.verify;
50 import static org.junit.Assert.assertTrue;
53 * Unit tests for the PartitionManager class.
55 public class PartitionManagerTest {
57 private final LeadershipEvent event
58 = new LeadershipEvent(LeadershipEvent.Type.LEADER_ELECTED,
59 new Leadership(ELECTION_PREFIX + "0",
62 private static final NodeId MY_NODE_ID = new NodeId("local");
63 private static final NodeId OTHER_NODE_ID = new NodeId("other");
64 private static final NodeId INACTIVE_NODE_ID = new NodeId("inactive");
66 private static final String ELECTION_PREFIX = "intent-partition-";
68 private LeadershipService leadershipService;
69 private LeadershipEventListener leaderListener;
71 private PartitionManager partitionManager;
75 leadershipService = createMock(LeadershipService.class);
77 leadershipService.addListener(anyObject(LeadershipEventListener.class));
78 expectLastCall().andDelegateTo(new TestLeadershipService());
79 for (int i = 0; i < PartitionManager.NUM_PARTITIONS; i++) {
80 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
81 .andReturn(CompletableFuture.completedFuture(null))
85 partitionManager = new PartitionManager()
86 .withScheduledExecutor(new NullScheduledExecutor());
88 partitionManager.clusterService = new TestClusterService();
89 partitionManager.leadershipService = leadershipService;
90 partitionManager.eventDispatcher = new TestEventDispatcher();
94 * Configures a mock leadership service to have the specified number of
95 * partitions owned by the local node and all other partitions owned by a
98 * @param numMine number of partitions that should be owned by the local node
100 private void setUpLeadershipService(int numMine) {
102 Map<String, Leadership> leaderBoard = new HashMap<>();
104 for (int i = 0; i < numMine; i++) {
105 expect(leadershipService.getLeader(ELECTION_PREFIX + i))
106 .andReturn(MY_NODE_ID).anyTimes();
107 leaderBoard.put(ELECTION_PREFIX + i,
108 new Leadership(ELECTION_PREFIX + i, MY_NODE_ID, 0, 0));
111 for (int i = numMine; i < PartitionManager.NUM_PARTITIONS; i++) {
112 expect(leadershipService.getLeader(ELECTION_PREFIX + i))
113 .andReturn(OTHER_NODE_ID).anyTimes();
115 leaderBoard.put(ELECTION_PREFIX + i,
116 new Leadership(ELECTION_PREFIX + i, OTHER_NODE_ID, 0, 0));
119 expect(leadershipService.getLeaderBoard()).andReturn(leaderBoard).anyTimes();
123 * Tests that the PartitionManager's activate method correctly runs for
124 * all the leader elections that it should.
127 public void testActivate() {
128 reset(leadershipService);
130 leadershipService.addListener(anyObject(LeadershipEventListener.class));
132 for (int i = 0; i < PartitionManager.NUM_PARTITIONS; i++) {
133 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
134 .andReturn(CompletableFuture.completedFuture(null))
138 replay(leadershipService);
140 partitionManager.activate();
142 verify(leadershipService);
146 * Tests that the isMine method returns the correct result based on the
147 * underlying leadership service data.
150 public void testIsMine() {
151 // We'll own only the first partition
152 setUpLeadershipService(1);
153 replay(leadershipService);
155 Key myKey = new ControllableHashKey(0);
156 Key notMyKey = new ControllableHashKey(1);
158 assertTrue(partitionManager.isMine(myKey));
159 assertFalse(partitionManager.isMine(notMyKey));
161 // Make us the owner of 4 partitions now
162 reset(leadershipService);
163 setUpLeadershipService(4);
164 replay(leadershipService);
166 assertTrue(partitionManager.isMine(myKey));
167 // notMyKey is now my key because because we're in control of that
169 assertTrue(partitionManager.isMine(notMyKey));
171 assertFalse(partitionManager.isMine(new ControllableHashKey(4)));
175 * Tests sending in LeadershipServiceEvents in the case when we have
176 * too many partitions. The event will trigger the partition manager to
177 * schedule a rebalancing activity.
180 public void testRebalanceScheduling() {
181 // We have all the partitions so we'll need to relinquish some
182 setUpLeadershipService(PartitionManager.NUM_PARTITIONS);
184 replay(leadershipService);
186 partitionManager.activate();
188 leaderListener.event(event);
190 assertTrue(partitionManager.rebalanceScheduled.get());
192 verify(leadershipService);
196 * Tests rebalance will trigger the right now of leadership withdraw calls.
199 public void testRebalance() {
200 // We have all the partitions so we'll need to relinquish some
201 setUpLeadershipService(PartitionManager.NUM_PARTITIONS);
203 expect(leadershipService.withdraw(anyString()))
204 .andReturn(CompletableFuture.completedFuture(null))
207 replay(leadershipService);
209 partitionManager.activate();
212 partitionManager.doRebalance();
214 verify(leadershipService);
218 * Tests that attempts to rebalance when the paritions are already
219 * evenly distributed does not result in any relinquish attempts.
222 public void testNoRebalance() {
223 // Partitions are already perfectly balanced among the two active instances
224 setUpLeadershipService(PartitionManager.NUM_PARTITIONS / 2);
225 replay(leadershipService);
227 partitionManager.activate();
230 partitionManager.doRebalance();
232 verify(leadershipService);
234 reset(leadershipService);
235 // We have a smaller share than we should
236 setUpLeadershipService(PartitionManager.NUM_PARTITIONS / 2 - 1);
237 replay(leadershipService);
240 partitionManager.doRebalance();
242 verify(leadershipService);
246 * LeadershipService that allows us to grab a reference to
247 * PartitionManager's LeadershipEventListener.
249 public class TestLeadershipService extends LeadershipServiceAdapter {
251 public void addListener(LeadershipEventListener listener) {
252 leaderListener = listener;
257 * ClusterService set up with a very simple cluster - 3 nodes, one is the
258 * current node, one is a different active node, and one is an inactive node.
260 private class TestClusterService extends ClusterServiceAdapter {
262 private final ControllerNode self =
263 new DefaultControllerNode(MY_NODE_ID, IpAddress.valueOf(1));
264 private final ControllerNode otherNode =
265 new DefaultControllerNode(OTHER_NODE_ID, IpAddress.valueOf(2));
266 private final ControllerNode inactiveNode =
267 new DefaultControllerNode(INACTIVE_NODE_ID, IpAddress.valueOf(3));
269 Set<ControllerNode> nodes;
271 public TestClusterService() {
272 nodes = new HashSet<>();
274 nodes.add(otherNode);
275 nodes.add(inactiveNode);
279 public ControllerNode getLocalNode() {
284 public Set<ControllerNode> getNodes() {
289 public ControllerNode getNode(NodeId nodeId) {
290 return nodes.stream()
291 .filter(c -> c.id().equals(nodeId))
297 public ControllerNode.State getState(NodeId nodeId) {
298 return nodeId.equals(INACTIVE_NODE_ID) ? ControllerNode.State.INACTIVE :
299 ControllerNode.State.ACTIVE;
304 * A key that always hashes to a value provided to the constructor. This
305 * allows us to control the hash of the key for unit tests.
307 private class ControllableHashKey extends Key {
309 protected ControllableHashKey(long hash) {
314 public int hashCode() {
315 return Objects.hash(hash());
319 public boolean equals(Object obj) {
320 if (!(obj instanceof ControllableHashKey)) {
324 ControllableHashKey that = (ControllableHashKey) obj;
326 return Objects.equals(this.hash(), that.hash());