blob: 0c92280242dbd100b8c01334bec9016b2fa6a199 [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;
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.HashMap;
37import java.util.HashSet;
38import java.util.Map;
39import java.util.Objects;
40import java.util.Set;
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 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
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
67 private static final String ELECTION_PREFIX = "intent-partition-";
68
69 private LeadershipService leadershipService;
70 private LeadershipEventListener leaderListener;
71
Madan Jampani1c965102016-01-13 14:34:16 -080072 private IntentPartitionManager 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 Jampani1c965102016-01-13 14:34:16 -080080 for (int i = 0; i < IntentPartitionManager.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 Jampani1c965102016-01-13 14:34:16 -080086 partitionManager = new IntentPartitionManager()
Jonathan Hart7061acd2015-03-04 13:15:32 -080087 .withScheduledExecutor(new NullScheduledExecutor());
88
89 partitionManager.clusterService = new TestClusterService();
90 partitionManager.leadershipService = leadershipService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070091 partitionManager.eventDispatcher = new TestEventDispatcher();
Jonathan Hart7061acd2015-03-04 13:15:32 -080092 }
93
94 /**
95 * Configures a mock leadership service to have the specified number of
96 * partitions owned by the local node and all other partitions owned by a
97 * (fake) remote node.
98 *
99 * @param numMine number of partitions that should be owned by the local node
100 */
101 private void setUpLeadershipService(int numMine) {
Madan Jampanide003d92015-05-11 17:14:20 -0700102
Jonathan Hart7061acd2015-03-04 13:15:32 -0800103 Map<String, Leadership> leaderBoard = new HashMap<>();
104
105 for (int i = 0; i < numMine; i++) {
106 expect(leadershipService.getLeader(ELECTION_PREFIX + i))
107 .andReturn(MY_NODE_ID).anyTimes();
108 leaderBoard.put(ELECTION_PREFIX + i,
Madan Jampani620f70d2016-01-30 22:22:47 -0800109 new Leadership(ELECTION_PREFIX + i,
110 new Leader(MY_NODE_ID, 0, 0),
111 Arrays.asList(MY_NODE_ID)));
Jonathan Hart7061acd2015-03-04 13:15:32 -0800112 }
113
Madan Jampani1c965102016-01-13 14:34:16 -0800114 for (int i = numMine; i < IntentPartitionManager.NUM_PARTITIONS; i++) {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800115 expect(leadershipService.getLeader(ELECTION_PREFIX + i))
116 .andReturn(OTHER_NODE_ID).anyTimes();
117
118 leaderBoard.put(ELECTION_PREFIX + i,
Madan Jampani620f70d2016-01-30 22:22:47 -0800119 new Leadership(ELECTION_PREFIX + i,
120 new Leader(OTHER_NODE_ID, 0, 0),
121 Arrays.asList(OTHER_NODE_ID)));
Jonathan Hart7061acd2015-03-04 13:15:32 -0800122 }
123
124 expect(leadershipService.getLeaderBoard()).andReturn(leaderBoard).anyTimes();
125 }
126
127 /**
128 * Tests that the PartitionManager's activate method correctly runs for
129 * all the leader elections that it should.
130 */
131 @Test
132 public void testActivate() {
133 reset(leadershipService);
134
135 leadershipService.addListener(anyObject(LeadershipEventListener.class));
136
Madan Jampani1c965102016-01-13 14:34:16 -0800137 for (int i = 0; i < IntentPartitionManager.NUM_PARTITIONS; i++) {
Madan Jampanide003d92015-05-11 17:14:20 -0700138 expect(leadershipService.runForLeadership(ELECTION_PREFIX + i))
Madan Jampani620f70d2016-01-30 22:22:47 -0800139 .andReturn(null)
Madan Jampanide003d92015-05-11 17:14:20 -0700140 .times(1);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800141 }
142
143 replay(leadershipService);
144
145 partitionManager.activate();
146
147 verify(leadershipService);
148 }
149
150 /**
151 * Tests that the isMine method returns the correct result based on the
152 * underlying leadership service data.
153 */
154 @Test
155 public void testIsMine() {
156 // We'll own only the first partition
157 setUpLeadershipService(1);
158 replay(leadershipService);
159
160 Key myKey = new ControllableHashKey(0);
161 Key notMyKey = new ControllableHashKey(1);
162
163 assertTrue(partitionManager.isMine(myKey));
164 assertFalse(partitionManager.isMine(notMyKey));
165
166 // Make us the owner of 4 partitions now
167 reset(leadershipService);
168 setUpLeadershipService(4);
169 replay(leadershipService);
170
171 assertTrue(partitionManager.isMine(myKey));
172 // notMyKey is now my key because because we're in control of that
173 // partition now
174 assertTrue(partitionManager.isMine(notMyKey));
175
176 assertFalse(partitionManager.isMine(new ControllableHashKey(4)));
177 }
178
179 /**
180 * Tests sending in LeadershipServiceEvents in the case when we have
181 * too many partitions. The event will trigger the partition manager to
Madan Jampani4732c1b2015-05-19 17:11:50 -0700182 * schedule a rebalancing activity.
Jonathan Hart7061acd2015-03-04 13:15:32 -0800183 */
184 @Test
Madan Jampani4732c1b2015-05-19 17:11:50 -0700185 public void testRebalanceScheduling() {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800186 // We have all the partitions so we'll need to relinquish some
Madan Jampani1c965102016-01-13 14:34:16 -0800187 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800188
Jonathan Hart7061acd2015-03-04 13:15:32 -0800189 replay(leadershipService);
190
191 partitionManager.activate();
192 // Send in the event
193 leaderListener.event(event);
194
Madan Jampani4732c1b2015-05-19 17:11:50 -0700195 assertTrue(partitionManager.rebalanceScheduled.get());
196
Jonathan Hart7061acd2015-03-04 13:15:32 -0800197 verify(leadershipService);
198 }
199
200 /**
Madan Jampani4732c1b2015-05-19 17:11:50 -0700201 * Tests rebalance will trigger the right now of leadership withdraw calls.
Jonathan Hart7061acd2015-03-04 13:15:32 -0800202 */
203 @Test
Madan Jampani4732c1b2015-05-19 17:11:50 -0700204 public void testRebalance() {
205 // We have all the partitions so we'll need to relinquish some
Madan Jampani1c965102016-01-13 14:34:16 -0800206 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS);
Madan Jampani4732c1b2015-05-19 17:11:50 -0700207
Madan Jampani620f70d2016-01-30 22:22:47 -0800208 leadershipService.withdraw(anyString());
209 expectLastCall().times(7);
Madan Jampani4732c1b2015-05-19 17:11:50 -0700210
211 replay(leadershipService);
212
213 partitionManager.activate();
214
215 // trigger rebalance
216 partitionManager.doRebalance();
217
218 verify(leadershipService);
219 }
220
221 /**
222 * Tests that attempts to rebalance when the paritions are already
223 * evenly distributed does not result in any relinquish attempts.
224 */
225 @Test
226 public void testNoRebalance() {
Jonathan Hart7061acd2015-03-04 13:15:32 -0800227 // Partitions are already perfectly balanced among the two active instances
Madan Jampani1c965102016-01-13 14:34:16 -0800228 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS / 2);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800229 replay(leadershipService);
230
231 partitionManager.activate();
232
Madan Jampani4732c1b2015-05-19 17:11:50 -0700233 // trigger rebalance
234 partitionManager.doRebalance();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800235
236 verify(leadershipService);
237
238 reset(leadershipService);
239 // We have a smaller share than we should
Madan Jampani1c965102016-01-13 14:34:16 -0800240 setUpLeadershipService(IntentPartitionManager.NUM_PARTITIONS / 2 - 1);
Jonathan Hart7061acd2015-03-04 13:15:32 -0800241 replay(leadershipService);
242
Madan Jampani4732c1b2015-05-19 17:11:50 -0700243 // trigger rebalance
244 partitionManager.doRebalance();
Jonathan Hart7061acd2015-03-04 13:15:32 -0800245
246 verify(leadershipService);
247 }
248
249 /**
250 * LeadershipService that allows us to grab a reference to
251 * PartitionManager's LeadershipEventListener.
252 */
253 public class TestLeadershipService extends LeadershipServiceAdapter {
254 @Override
255 public void addListener(LeadershipEventListener listener) {
256 leaderListener = listener;
257 }
258 }
259
260 /**
261 * ClusterService set up with a very simple cluster - 3 nodes, one is the
262 * current node, one is a different active node, and one is an inactive node.
263 */
264 private class TestClusterService extends ClusterServiceAdapter {
265
266 private final ControllerNode self =
267 new DefaultControllerNode(MY_NODE_ID, IpAddress.valueOf(1));
268 private final ControllerNode otherNode =
269 new DefaultControllerNode(OTHER_NODE_ID, IpAddress.valueOf(2));
270 private final ControllerNode inactiveNode =
271 new DefaultControllerNode(INACTIVE_NODE_ID, IpAddress.valueOf(3));
272
273 Set<ControllerNode> nodes;
274
275 public TestClusterService() {
276 nodes = new HashSet<>();
277 nodes.add(self);
278 nodes.add(otherNode);
279 nodes.add(inactiveNode);
280 }
281
282 @Override
283 public ControllerNode getLocalNode() {
284 return self;
285 }
286
287 @Override
288 public Set<ControllerNode> getNodes() {
289 return nodes;
290 }
291
292 @Override
293 public ControllerNode getNode(NodeId nodeId) {
294 return nodes.stream()
295 .filter(c -> c.id().equals(nodeId))
296 .findFirst()
297 .get();
298 }
299
300 @Override
301 public ControllerNode.State getState(NodeId nodeId) {
302 return nodeId.equals(INACTIVE_NODE_ID) ? ControllerNode.State.INACTIVE :
303 ControllerNode.State.ACTIVE;
304 }
305 }
306
307 /**
308 * A key that always hashes to a value provided to the constructor. This
309 * allows us to control the hash of the key for unit tests.
310 */
311 private class ControllableHashKey extends Key {
312
313 protected ControllableHashKey(long hash) {
314 super(hash);
315 }
316
317 @Override
318 public int hashCode() {
319 return Objects.hash(hash());
320 }
321
322 @Override
323 public boolean equals(Object obj) {
324 if (!(obj instanceof ControllableHashKey)) {
325 return false;
326 }
327
328 ControllableHashKey that = (ControllableHashKey) obj;
329
330 return Objects.equals(this.hash(), that.hash());
331 }
Simon Hunte2b6a2b2015-10-27 11:38:51 -0700332
333 @Override
334 public int compareTo(Key o) {
335 Long thisHash = hash();
336 return thisHash.compareTo(o.hash());
337 }
Jonathan Hart7061acd2015-03-04 13:15:32 -0800338 }
339}