blob: 5ba0c7c2e2e123db08981c5f267f3caeb6284f39 [file] [log] [blame]
Jonathan Hart7061acd2015-03-04 13:15:32 -08001/*
2 * Copyright 2015 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 */
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;
25import org.onosproject.cluster.Leadership;
26import org.onosproject.cluster.LeadershipEvent;
27import org.onosproject.cluster.LeadershipEventListener;
28import org.onosproject.cluster.LeadershipService;
29import org.onosproject.cluster.LeadershipServiceAdapter;
30import org.onosproject.cluster.NodeId;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070031import org.onosproject.common.event.impl.TestEventDispatcher;
Jonathan Hart7061acd2015-03-04 13:15:32 -080032import org.onosproject.net.intent.Key;
33
34import java.util.HashMap;
35import java.util.HashSet;
36import java.util.Map;
37import java.util.Objects;
38import java.util.Set;
Madan Jampanide003d92015-05-11 17:14:20 -070039import java.util.concurrent.CompletableFuture;
Jonathan Hart7061acd2015-03-04 13:15:32 -080040
41import 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 Jampani1c965102016-01-13 14:34:16 -080053 * Unit tests for the IntentPartitionManager class.
Jonathan Hart7061acd2015-03-04 13:15:32 -080054 */
Madan Jampani1c965102016-01-13 14:34:16 -080055public class IntentPartitionManagerTest {
Jonathan Hart7061acd2015-03-04 13:15:32 -080056
57 private final LeadershipEvent event
58 = new LeadershipEvent(LeadershipEvent.Type.LEADER_ELECTED,
59 new Leadership(ELECTION_PREFIX + "0",
60 MY_NODE_ID, 0, 0));
61
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");
65
66 private static final String ELECTION_PREFIX = "intent-partition-";
67
68 private LeadershipService leadershipService;
69 private LeadershipEventListener leaderListener;
70
Madan Jampani1c965102016-01-13 14:34:16 -080071 private IntentPartitionManager partitionManager;
Jonathan Hart7061acd2015-03-04 13:15:32 -080072
73 @Before
74 public void setUp() {
75 leadershipService = createMock(LeadershipService.class);
76
77 leadershipService.addListener(anyObject(LeadershipEventListener.class));
78 expectLastCall().andDelegateTo(new TestLeadershipService());
Madan Jampani1c965102016-01-13 14:34:16 -080079 for (int i = 0; i < IntentPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampanide003d92015-05-11 17:14:20 -070080 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
81 .andReturn(CompletableFuture.completedFuture(null))
82 .times(1);
83 }
Jonathan Hart7061acd2015-03-04 13:15:32 -080084
Madan Jampani1c965102016-01-13 14:34:16 -080085 partitionManager = new IntentPartitionManager()
Jonathan Hart7061acd2015-03-04 13:15:32 -080086 .withScheduledExecutor(new NullScheduledExecutor());
87
88 partitionManager.clusterService = new TestClusterService();
89 partitionManager.leadershipService = leadershipService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070090 partitionManager.eventDispatcher = new TestEventDispatcher();
Jonathan Hart7061acd2015-03-04 13:15:32 -080091 }
92
93 /**
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
96 * (fake) remote node.
97 *
98 * @param numMine number of partitions that should be owned by the local node
99 */
100 private void setUpLeadershipService(int numMine) {
Madan Jampanide003d92015-05-11 17:14:20 -0700101
Jonathan Hart7061acd2015-03-04 13:15:32 -0800102 Map<String, Leadership> leaderBoard = new HashMap<>();
103
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));
109 }
110
Madan Jampani1c965102016-01-13 14:34:16 -0800111 for (int i = numMine; i < IntentPartitionManager.NUM_PARTITIONS; i++) {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800112 expect(leadershipService.getLeader(ELECTION_PREFIX + i))
113 .andReturn(OTHER_NODE_ID).anyTimes();
114
115 leaderBoard.put(ELECTION_PREFIX + i,
116 new Leadership(ELECTION_PREFIX + i, OTHER_NODE_ID, 0, 0));
117 }
118
119 expect(leadershipService.getLeaderBoard()).andReturn(leaderBoard).anyTimes();
120 }
121
122 /**
123 * Tests that the PartitionManager's activate method correctly runs for
124 * all the leader elections that it should.
125 */
126 @Test
127 public void testActivate() {
128 reset(leadershipService);
129
130 leadershipService.addListener(anyObject(LeadershipEventListener.class));
131
Madan Jampani1c965102016-01-13 14:34:16 -0800132 for (int i = 0; i < IntentPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampanide003d92015-05-11 17:14:20 -0700133 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
134 .andReturn(CompletableFuture.completedFuture(null))
135 .times(1);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800136 }
137
138 replay(leadershipService);
139
140 partitionManager.activate();
141
142 verify(leadershipService);
143 }
144
145 /**
146 * Tests that the isMine method returns the correct result based on the
147 * underlying leadership service data.
148 */
149 @Test
150 public void testIsMine() {
151 // We'll own only the first partition
152 setUpLeadershipService(1);
153 replay(leadershipService);
154
155 Key myKey = new ControllableHashKey(0);
156 Key notMyKey = new ControllableHashKey(1);
157
158 assertTrue(partitionManager.isMine(myKey));
159 assertFalse(partitionManager.isMine(notMyKey));
160
161 // Make us the owner of 4 partitions now
162 reset(leadershipService);
163 setUpLeadershipService(4);
164 replay(leadershipService);
165
166 assertTrue(partitionManager.isMine(myKey));
167 // notMyKey is now my key because because we're in control of that
168 // partition now
169 assertTrue(partitionManager.isMine(notMyKey));
170
171 assertFalse(partitionManager.isMine(new ControllableHashKey(4)));
172 }
173
174 /**
175 * Tests sending in LeadershipServiceEvents in the case when we have
176 * too many partitions. The event will trigger the partition manager to
Madan Jampani4732c1b2015-05-19 17:11:50 -0700177 * schedule a rebalancing activity.
Jonathan Hart7061acd2015-03-04 13:15:32 -0800178 */
179 @Test
Madan Jampani4732c1b2015-05-19 17:11:50 -0700180 public void testRebalanceScheduling() {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800181 // We have all the partitions so we'll need to relinquish some
Madan Jampani1c965102016-01-13 14:34:16 -0800182 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800183
Jonathan Hart7061acd2015-03-04 13:15:32 -0800184 replay(leadershipService);
185
186 partitionManager.activate();
187 // Send in the event
188 leaderListener.event(event);
189
Madan Jampani4732c1b2015-05-19 17:11:50 -0700190 assertTrue(partitionManager.rebalanceScheduled.get());
191
Jonathan Hart7061acd2015-03-04 13:15:32 -0800192 verify(leadershipService);
193 }
194
195 /**
Madan Jampani4732c1b2015-05-19 17:11:50 -0700196 * Tests rebalance will trigger the right now of leadership withdraw calls.
Jonathan Hart7061acd2015-03-04 13:15:32 -0800197 */
198 @Test
Madan Jampani4732c1b2015-05-19 17:11:50 -0700199 public void testRebalance() {
200 // We have all the partitions so we'll need to relinquish some
Madan Jampani1c965102016-01-13 14:34:16 -0800201 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS);
Madan Jampani4732c1b2015-05-19 17:11:50 -0700202
203 expect(leadershipService.withdraw(anyString()))
204 .andReturn(CompletableFuture.completedFuture(null))
205 .times(7);
206
207 replay(leadershipService);
208
209 partitionManager.activate();
210
211 // trigger rebalance
212 partitionManager.doRebalance();
213
214 verify(leadershipService);
215 }
216
217 /**
218 * Tests that attempts to rebalance when the paritions are already
219 * evenly distributed does not result in any relinquish attempts.
220 */
221 @Test
222 public void testNoRebalance() {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800223 // Partitions are already perfectly balanced among the two active instances
Madan Jampani1c965102016-01-13 14:34:16 -0800224 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS / 2);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800225 replay(leadershipService);
226
227 partitionManager.activate();
228
Madan Jampani4732c1b2015-05-19 17:11:50 -0700229 // trigger rebalance
230 partitionManager.doRebalance();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800231
232 verify(leadershipService);
233
234 reset(leadershipService);
235 // We have a smaller share than we should
Madan Jampani1c965102016-01-13 14:34:16 -0800236 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS / 2 - 1);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800237 replay(leadershipService);
238
Madan Jampani4732c1b2015-05-19 17:11:50 -0700239 // trigger rebalance
240 partitionManager.doRebalance();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800241
242 verify(leadershipService);
243 }
244
245 /**
246 * LeadershipService that allows us to grab a reference to
247 * PartitionManager's LeadershipEventListener.
248 */
249 public class TestLeadershipService extends LeadershipServiceAdapter {
250 @Override
251 public void addListener(LeadershipEventListener listener) {
252 leaderListener = listener;
253 }
254 }
255
256 /**
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.
259 */
260 private class TestClusterService extends ClusterServiceAdapter {
261
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));
268
269 Set<ControllerNode> nodes;
270
271 public TestClusterService() {
272 nodes = new HashSet<>();
273 nodes.add(self);
274 nodes.add(otherNode);
275 nodes.add(inactiveNode);
276 }
277
278 @Override
279 public ControllerNode getLocalNode() {
280 return self;
281 }
282
283 @Override
284 public Set<ControllerNode> getNodes() {
285 return nodes;
286 }
287
288 @Override
289 public ControllerNode getNode(NodeId nodeId) {
290 return nodes.stream()
291 .filter(c -> c.id().equals(nodeId))
292 .findFirst()
293 .get();
294 }
295
296 @Override
297 public ControllerNode.State getState(NodeId nodeId) {
298 return nodeId.equals(INACTIVE_NODE_ID) ? ControllerNode.State.INACTIVE :
299 ControllerNode.State.ACTIVE;
300 }
301 }
302
303 /**
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.
306 */
307 private class ControllableHashKey extends Key {
308
309 protected ControllableHashKey(long hash) {
310 super(hash);
311 }
312
313 @Override
314 public int hashCode() {
315 return Objects.hash(hash());
316 }
317
318 @Override
319 public boolean equals(Object obj) {
320 if (!(obj instanceof ControllableHashKey)) {
321 return false;
322 }
323
324 ControllableHashKey that = (ControllableHashKey) obj;
325
326 return Objects.equals(this.hash(), that.hash());
327 }
Simon Hunte2b6a2b2015-10-27 11:38:51 -0700328
329 @Override
330 public int compareTo(Key o) {
331 Long thisHash = hash();
332 return thisHash.compareTo(o.hash());
333 }
Jonathan Hart7061acd2015-03-04 13:15:32 -0800334 }
335}