Fixing intermittent Junit failure
[genius.git] / idmanager / idmanager-impl / src / test / java / org / opendaylight / genius / idmanager / test / IdManagerTest.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.genius.idmanager.test;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Matchers.anyObject;
14 import static org.mockito.Matchers.eq;
15 import static org.mockito.Mockito.doAnswer;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.when;
18
19 import com.google.common.base.Optional;
20 import com.google.common.util.concurrent.Futures;
21
22 import java.net.UnknownHostException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.CopyOnWriteArraySet;
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors;
33 import java.util.concurrent.Future;
34 import java.util.concurrent.TimeUnit;
35
36 import org.junit.Before;
37 import org.junit.Ignore;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.mockito.Matchers;
41 import org.mockito.Mock;
42 import org.mockito.runners.MockitoJUnitRunner;
43 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
44 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
45 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
46 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
47 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
48 import org.opendaylight.genius.idmanager.IdLocalPool;
49 import org.opendaylight.genius.idmanager.IdManager;
50 import org.opendaylight.genius.idmanager.IdUtils;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInputBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdPools;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdPoolsBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPool;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.AvailableIdsHolder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.AvailableIdsHolderBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPools;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPoolsBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPoolsKey;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntries;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntriesBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntriesKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ReleasedIdsHolder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ReleasedIdsHolderBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.released.ids.DelayedIdEntries;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.released.ids.DelayedIdEntriesBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockInput;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockInput;
80 import org.opendaylight.yangtools.yang.binding.DataObject;
81 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
82 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
83 import org.opendaylight.yangtools.yang.common.RpcError;
84 import org.opendaylight.yangtools.yang.common.RpcResult;
85 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
86
87 @RunWith(MockitoJUnitRunner.class)
88 public class IdManagerTest {
89
90     ConcurrentHashMap<InstanceIdentifier<?>, DataObject> configDataStore = new ConcurrentHashMap<>();
91     @Mock DataBroker dataBroker;
92     @Mock ReadOnlyTransaction mockReadTx;
93     @Mock WriteTransaction mockWriteTx;
94     @Mock LockManagerService lockManager;
95     Future<RpcResult<Void>> rpcResult;
96     IdUtils idUtils;
97     IdManager idManager;
98     IdPool globalIdPool;
99     String allocateIdPoolName = "allocateIdTest";
100     InstanceIdentifier<IdPool> parentPoolIdentifier;
101     InstanceIdentifier<IdPool> localPoolIdentifier;
102     InstanceIdentifier<ChildPools> childPoolIdentifier;
103     final String poolName = "test-pool";
104     int idStart = 100;
105     int idEnd = 200;
106     int blockSize = 2;
107     String idKey = "test-key";
108     String localPoolName;
109     long idValue = 2;
110
111     @Before
112     public void setUp() throws Exception {
113         idUtils = new IdUtils();
114         localPoolName = idUtils.getLocalPoolName(poolName);
115
116         parentPoolIdentifier = buildInstanceIdentifier(poolName);
117         localPoolIdentifier = buildInstanceIdentifier(localPoolName);
118         childPoolIdentifier = buildChildPoolInstanceIdentifier(poolName, localPoolName);
119     }
120
121     private void setupMocks(List<IdPool> idPools) throws ReadFailedException, UnknownHostException {
122         when(dataBroker.newReadOnlyTransaction()).thenReturn(mockReadTx);
123         when(dataBroker.newWriteOnlyTransaction()).thenReturn(mockWriteTx);
124         when(lockManager.lock(any(LockInput.class)))
125                 .thenReturn(Futures.immediateFuture(RpcResultBuilder.<Void>success().build()));
126         when(lockManager.unlock(any(UnlockInput.class)))
127                 .thenReturn(Futures.immediateFuture(RpcResultBuilder.<Void>success().build()));
128         doReturn(Futures.immediateCheckedFuture(null)).when(mockWriteTx).submit();
129         doAnswer(invocation -> {
130             configDataStore.put(invocation.getArgumentAt(1, KeyedInstanceIdentifier.class),
131                     invocation.getArgumentAt(2, IdPool.class));
132             return null;
133         }).when(mockWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), Matchers.any(), Matchers.any(), eq(true));
134         doAnswer(invocation -> {
135             configDataStore.put(invocation.getArgumentAt(1, KeyedInstanceIdentifier.class),
136                     invocation.getArgumentAt(2, IdPool.class));
137             return null;
138         }).when(mockWriteTx).merge(eq(LogicalDatastoreType.CONFIGURATION), Matchers.any(), Matchers.any(), eq(true));
139         doAnswer(invocation -> {
140             configDataStore.put(invocation.getArgumentAt(1, KeyedInstanceIdentifier.class),
141                     invocation.getArgumentAt(2, IdPool.class));
142             return null;
143         }).when(mockWriteTx).merge(eq(LogicalDatastoreType.CONFIGURATION), Matchers.any(), Matchers.any());
144         doAnswer(invocation -> {
145             configDataStore.remove(invocation.getArgumentAt(1, KeyedInstanceIdentifier.class));
146             return null;
147         }).when(mockWriteTx).delete(eq(LogicalDatastoreType.CONFIGURATION), Matchers.<InstanceIdentifier<IdPool>>any());
148
149         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockReadTx)
150                 .read(eq(LogicalDatastoreType.CONFIGURATION), anyObject());
151         if (idPools != null && !idPools.isEmpty()) {
152             Optional<IdPools> optionalIdPools = Optional.of(new IdPoolsBuilder().setIdPool(idPools).build());
153             doReturn(Futures.immediateCheckedFuture(optionalIdPools)).when(mockReadTx)
154                     .read(LogicalDatastoreType.CONFIGURATION, idUtils.getIdPools());
155         }
156         idManager = new IdManager(dataBroker, lockManager, idUtils);
157     }
158
159     @Test
160     public void testCreateIdPool() throws Exception {
161         setupMocks(null);
162         CreateIdPoolInput createPoolTest = buildCreateIdPool(poolName, idStart, idEnd);
163         long expectedBlockSize = idUtils.computeBlockSize(idStart, idEnd);
164
165         Future<RpcResult<Void>> result = idManager.createIdPool(createPoolTest);
166         IdPool pool;
167         assertTrue(result.get().isSuccessful());
168         // Just to ensure the local pool is also written. Even if it is not triggered Test case will pass.
169         waitUntilJobIsDone();
170         assertTrue(configDataStore.size() > 0);
171         DataObject dataObject = configDataStore.get(localPoolIdentifier);
172         if (dataObject instanceof IdPool) {
173             pool = (IdPool) dataObject;
174             assertEquals(localPoolName, pool.getPoolName());
175             assertEquals(createPoolTest.getPoolName(), pool.getParentPoolName());
176             assertEquals(idStart, pool.getAvailableIdsHolder().getStart().intValue());
177             assertEquals(idStart + expectedBlockSize - 1, pool.getAvailableIdsHolder().getEnd().intValue());
178             assertEquals(idStart - 1, pool.getAvailableIdsHolder().getCursor().intValue());
179             assertEquals(30, pool.getReleasedIdsHolder().getDelayedTimeSec().longValue());
180             assertEquals(0, pool.getReleasedIdsHolder().getAvailableIdCount().longValue());
181             assertEquals(expectedBlockSize, pool.getBlockSize().longValue());
182         }
183         dataObject = configDataStore.get(parentPoolIdentifier);
184         if (dataObject instanceof IdPool) {
185             pool = (IdPool) dataObject;
186             assertEquals(createPoolTest.getPoolName(), pool.getPoolName());
187             assertEquals(0, pool.getReleasedIdsHolder().getDelayedTimeSec().longValue());
188             assertEquals(0, pool.getReleasedIdsHolder().getAvailableIdCount().longValue());
189             assertEquals(createPoolTest.getLow(), pool.getAvailableIdsHolder().getStart());
190             assertEquals(createPoolTest.getHigh(), pool.getAvailableIdsHolder().getEnd());
191             assertEquals(createPoolTest.getLow() - 1, pool.getAvailableIdsHolder().getCursor().intValue());
192             assertEquals(expectedBlockSize, pool.getBlockSize().longValue());
193         }
194         dataObject = configDataStore.get(childPoolIdentifier);
195         if (dataObject instanceof ChildPools) {
196             ChildPools childPool = (ChildPools) dataObject;
197             assertEquals(localPoolName, childPool.getChildPoolName());
198         }
199     }
200
201     @Test
202     public void testAllocateId() throws Exception {
203         List<IdPool> listOfIdPool = new ArrayList<>();
204         IdPool localIdPool = buildLocalIdPool(blockSize, idStart, idStart + blockSize - 1, idStart - 1, localPoolName,
205                 poolName).build();
206         listOfIdPool.add(localIdPool);
207         IdPool globalIdPool = buildGlobalIdPool(poolName, idStart, idEnd, idStart + blockSize,
208                 buildChildPool(localPoolName)).build();
209         listOfIdPool.add(globalIdPool);
210         setupMocks(listOfIdPool);
211         doReturn(Futures.immediateCheckedFuture(Optional.of(globalIdPool))).when(mockReadTx).read(
212                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
213
214         AllocateIdInput allocateIdInput = buildAllocateId(poolName, idKey);
215         Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(allocateIdInput);
216         assertTrue(result.get().isSuccessful());
217         waitUntilJobIsDone();
218         assertTrue(configDataStore.size() > 0);
219         DataObject dataObject = configDataStore.get(localPoolIdentifier);
220         if (dataObject instanceof IdPool) {
221             IdPool pool = (IdPool) dataObject;
222             assertEquals(localPoolName, pool.getPoolName());
223             assertEquals(idStart, pool.getAvailableIdsHolder().getStart().intValue());
224             assertEquals(idStart + blockSize - 1 , pool.getAvailableIdsHolder().getEnd().intValue());
225             assertEquals(idStart, pool.getAvailableIdsHolder().getCursor().intValue());
226         }
227         dataObject = configDataStore.get(parentPoolIdentifier);
228         if (dataObject instanceof IdPool) {
229             IdPool parentPool = (IdPool) dataObject;
230             assertEquals(1, parentPool.getIdEntries().size());
231         }
232         dataObject = configDataStore.get(childPoolIdentifier);
233         if (dataObject instanceof ChildPools) {
234             ChildPools childPool = (ChildPools) dataObject;
235             assertEquals(localPoolName, childPool.getChildPoolName());
236         }
237     }
238
239     @Test
240     public void testReleaseId() throws Exception {
241         List<IdEntries> idEntries = new ArrayList<>();
242         List<Long> idValuesList = new ArrayList<>();
243         idValuesList.add(idValue);
244
245         List<IdPool> listOfIdPool = new ArrayList<>();
246         IdPool expectedLocalPool = buildLocalIdPool(blockSize, idStart, idStart + blockSize - 1, idStart - 1,
247                 localPoolName, poolName).build();
248         IdPool globalIdPool = buildGlobalIdPool(poolName, idStart, idEnd, blockSize, buildChildPool(localPoolName))
249                 .setIdEntries(idEntries).build();
250         listOfIdPool.add(expectedLocalPool);
251         listOfIdPool.add(globalIdPool);
252         setupMocks(listOfIdPool);
253         doReturn(Futures.immediateCheckedFuture(Optional.of(globalIdPool))).when(mockReadTx).read(
254                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
255
256         InstanceIdentifier<IdEntries> idEntriesIdentifier = buildIdEntriesIdentifier(parentPoolIdentifier, idKey);
257         Optional<IdEntries> expectedIdEntry = Optional.of(buildIdEntry(idKey, idValuesList));
258         doReturn(Futures.immediateCheckedFuture(expectedIdEntry)).when(mockReadTx).read(
259                 LogicalDatastoreType.CONFIGURATION, idEntriesIdentifier);
260
261         ReleaseIdInput releaseIdInput = createReleaseIdInput(poolName, idKey);
262         Future<RpcResult<Void>> result = idManager.releaseId(releaseIdInput);
263         assertTrue(result.get().isSuccessful());
264         waitUntilJobIsDone();
265
266         assertTrue(configDataStore.size() > 0);
267         DataObject dataObject = configDataStore.get(localPoolIdentifier);
268         if (dataObject instanceof IdPool) {
269             IdPool pool = (IdPool) dataObject;
270             assertEquals(1, pool.getReleasedIdsHolder().getAvailableIdCount().intValue());
271             assertEquals(idValue, pool.getReleasedIdsHolder().getDelayedIdEntries().get(0).getId().intValue());
272         }
273         dataObject = configDataStore.get(parentPoolIdentifier);
274         if (dataObject instanceof IdPool) {
275             IdPool parentPool = (IdPool) dataObject;
276             assertEquals(0, parentPool.getIdEntries().size());
277         }
278         dataObject = configDataStore.get(childPoolIdentifier);
279         if (dataObject instanceof ChildPools) {
280             ChildPools childPool = (ChildPools) dataObject;
281             assertEquals(localPoolName, childPool.getChildPoolName());
282         }
283     }
284
285     /**
286      * Ignoring this test case since cleanup task gets scheduled only after 30
287      * seconds. Therefore in order to validate the pool state the test has to
288      * wait for at least 30 seconds.
289      */
290     @Ignore
291     public void testCleanupReleasedIds() throws Exception {
292         List<Long> idValues = Arrays.asList(1L, 2L, 3L, 4L, 5L);
293         IdEntries idEntry = buildIdEntry(idKey, idValues);
294         List<IdEntries> listOfIdEntries = new ArrayList<>();
295         listOfIdEntries.add(idEntry);
296
297         IdPool globalIdPool = buildGlobalIdPool(poolName, idStart, idEnd, blockSize, buildChildPool(localPoolName))
298                 .setIdEntries(listOfIdEntries).build();
299         IdPool expectedLocalPool = buildLocalIdPool(blockSize, idStart, idStart + blockSize - 1, idStart - 1,
300                 localPoolName, poolName).build();
301         List<IdPool> listOfIdPool = new ArrayList<>();
302
303         listOfIdPool.add(expectedLocalPool);
304         listOfIdPool.add(globalIdPool);
305         setupMocks(listOfIdPool);
306
307         Optional<IdPool> expectedGlobalPool = Optional.of(globalIdPool);
308         doReturn(Futures.immediateCheckedFuture(expectedGlobalPool)).when(mockReadTx).read(
309                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
310
311         InstanceIdentifier<IdEntries> idEntriesIdentifier = buildIdEntriesIdentifier(parentPoolIdentifier, idKey);
312         Optional<IdEntries> expectedIdEntry = Optional.of(idEntry);
313         doReturn(Futures.immediateCheckedFuture(expectedIdEntry)).when(mockReadTx).read(
314                 LogicalDatastoreType.CONFIGURATION, idEntriesIdentifier);
315
316         ReleasedIdsHolder releaseIdsHolder = createReleasedIdsHolder(0, new ArrayList<>(), 0);
317         InstanceIdentifier<ReleasedIdsHolder> releaseHolderIdentifier = buildReleaseIdsIdentifier(poolName);
318         doReturn(Futures.immediateCheckedFuture(Optional.of(releaseIdsHolder))).when(mockReadTx).read(
319                 LogicalDatastoreType.CONFIGURATION, releaseHolderIdentifier);
320
321         ReleaseIdInput releaseIdInput = createReleaseIdInput(poolName, idKey);
322         Future<RpcResult<Void>> result = idManager.releaseId(releaseIdInput);
323         Thread.sleep(40000);
324         assertTrue(result.get().isSuccessful());
325         assertTrue(configDataStore.size() > 0);
326
327         DataObject dataObject = configDataStore.get(localPoolIdentifier);
328         if (dataObject instanceof IdPool) {
329             IdPool pool = (IdPool) dataObject;
330             assertEquals(2, pool.getReleasedIdsHolder().getAvailableIdCount().intValue());
331         }
332
333         dataObject = configDataStore.get(parentPoolIdentifier);
334         if (dataObject instanceof IdPool) {
335             IdPool parentPool = (IdPool) dataObject;
336             assertEquals(0, parentPool.getIdEntries().size());
337         }
338
339         dataObject = configDataStore.get(childPoolIdentifier);
340         if (dataObject instanceof ChildPools) {
341             ChildPools childPool = (ChildPools) dataObject;
342             assertEquals(localPoolName, childPool.getChildPoolName());
343         }
344
345         InstanceIdentifier<ReleasedIdsHolder> releaseIdsIdentifier = buildReleaseIdsIdentifier(poolName);
346         dataObject = configDataStore.get(releaseIdsIdentifier);
347         if (dataObject instanceof ReleasedIdsHolder) {
348             ReleasedIdsHolder releasedIds = (ReleasedIdsHolder) dataObject;
349             assertEquals(3, releasedIds.getAvailableIdCount().intValue());
350             assertEquals(3, releasedIds.getDelayedIdEntries().size());
351         }
352     }
353
354     @Test
355     public void testAllocateIdBlockFromReleasedIds() throws Exception {
356         List<DelayedIdEntries> delayedIdEntries = buildDelayedIdEntries(new long[] {150, 151, 152});
357         ReleasedIdsHolder expectedReleasedIds = createReleasedIdsHolder(3, delayedIdEntries , 0);
358         IdPool globalIdPool = buildGlobalIdPool(poolName, idStart, idEnd, blockSize, buildChildPool(localPoolName))
359                 .setReleasedIdsHolder(expectedReleasedIds).build();
360         IdPool localPool = buildLocalIdPool(blockSize, idStart, idStart + blockSize - 1, idStart + blockSize - 1,
361                 localPoolName, poolName).build();
362         Optional<IdPool> expected = Optional.of(globalIdPool);
363         List<IdPool> listOfIdPool = new ArrayList<>();
364         listOfIdPool.add(localPool);
365         listOfIdPool.add(globalIdPool);
366         InstanceIdentifier<IdPool> parentPoolIdentifier = buildInstanceIdentifier(poolName);
367         doReturn(Futures.immediateCheckedFuture(expected)).when(mockReadTx).read(
368                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
369
370         setupMocks(listOfIdPool);
371         doReturn(Futures.immediateCheckedFuture(Optional.of(globalIdPool))).when(mockReadTx).read(
372                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
373
374         AllocateIdInput allocateIdInput = buildAllocateId(poolName, idKey);
375         Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(allocateIdInput);
376         assertTrue(result.get().isSuccessful());
377         waitUntilJobIsDone();
378
379         assertTrue(configDataStore.size() > 0);
380         InstanceIdentifier<IdPool> localPoolIdentifier = buildInstanceIdentifier(localPoolName);
381         DataObject dataObject = configDataStore.get(localPoolIdentifier);
382         if (dataObject instanceof IdPool) {
383             IdPool pool = (IdPool) dataObject;
384             assertEquals(localPoolName, pool.getPoolName());
385             assertEquals(1, pool.getReleasedIdsHolder().getDelayedIdEntries().size());
386             assertEquals(1, pool.getReleasedIdsHolder().getAvailableIdCount().intValue());
387         }
388
389         InstanceIdentifier<ReleasedIdsHolder> releaseIdsIdentifier = buildReleaseIdsIdentifier(poolName);
390         dataObject = configDataStore.get(releaseIdsIdentifier);
391         if (dataObject instanceof ReleasedIdsHolder) {
392             ReleasedIdsHolder releasedIds = (ReleasedIdsHolder) dataObject;
393             assertEquals(1, releasedIds.getAvailableIdCount().intValue());
394             assertEquals(1, releasedIds.getDelayedIdEntries().size());
395         }
396
397         InstanceIdentifier<ChildPools> childPoolIdentifier = buildChildPoolInstanceIdentifier(poolName, localPoolName);
398         dataObject = configDataStore.get(childPoolIdentifier);
399         if (dataObject instanceof ChildPools) {
400             ChildPools childPool = (ChildPools) dataObject;
401             assertEquals(localPoolName, childPool.getChildPoolName());
402         }
403     }
404
405     @Test
406     public void testDeletePool() throws Exception {
407         IdPool globalIdPool = buildGlobalIdPool(poolName, idStart, idEnd, blockSize, buildChildPool(localPoolName))
408                 .build();
409         IdPool localPool = buildLocalIdPool(blockSize, idStart, idStart + blockSize - 1, idStart + blockSize - 1,
410                 localPoolName, poolName).build();
411         List<IdPool> listOfIdPool = new ArrayList<>();
412         listOfIdPool.add(localPool);
413         listOfIdPool.add(globalIdPool);
414         // Pre-loading the map so that we can remove it when it is removed from DS.
415         configDataStore.put(parentPoolIdentifier, globalIdPool);
416         configDataStore.put(localPoolIdentifier, localPool);
417         setupMocks(listOfIdPool);
418         Optional<IdPool> expected = Optional.of(globalIdPool);
419         doReturn(Futures.immediateCheckedFuture(expected)).when(mockReadTx).read(
420                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
421         DeleteIdPoolInput deleteIdPoolInput = createDeleteIdPoolInput(poolName);
422         Future<RpcResult<Void>> result = idManager.deleteIdPool(deleteIdPoolInput);
423         waitUntilJobIsDone();
424         assertTrue(result.get().isSuccessful());
425         assertTrue(configDataStore.size() == 0);
426         DataObject dataObject = configDataStore.get(localPoolIdentifier);
427         assertEquals(dataObject, null);
428         dataObject = configDataStore.get(parentPoolIdentifier);
429         assertEquals(dataObject, null);
430     }
431
432     @Test
433     public void testMultithreadedIdAllocationFromAvailableIds() throws Exception {
434         setupMockForMultiThreads(false);
435         int numberOfTasks = 3;
436         CountDownLatch latch = new CountDownLatch(numberOfTasks);
437         Set<Long> idSet = new CopyOnWriteArraySet<>();
438         requestIdsConcurrently(latch, numberOfTasks, idSet, false);
439         latch.await();
440         waitUntilJobIsDone();
441         DataObject dataObject = configDataStore.get(localPoolIdentifier);
442         if (dataObject instanceof IdPool) {
443             IdPool pool = (IdPool) dataObject;
444             assertTrue(idStart + blockSize - 1 <= pool.getAvailableIdsHolder().getCursor());
445         }
446     }
447
448     @Test
449     public void testMultithreadedIdAllocationFromReleasedIds() throws Exception {
450         setupMockForMultiThreads(true);
451         // Check if the available id count is 3.
452         java.util.Optional<IdLocalPool> idLocalPool = idManager.getIdLocalPool(poolName);
453         assertTrue(idLocalPool.isPresent());
454         assertTrue(idLocalPool.get().getReleasedIds().getAvailableIdCount() == 3);
455         int numberOfTasks = 3;
456         CountDownLatch latch = new CountDownLatch(numberOfTasks);
457         Set<Long> idSet = new CopyOnWriteArraySet<>();
458         requestIdsConcurrently(latch, numberOfTasks, idSet, false);
459         latch.await();
460         waitUntilJobIsDone();
461         // Check if the available id count is 0.
462         idLocalPool = idManager.getIdLocalPool(poolName);
463         assertTrue(idLocalPool.isPresent());
464         assertTrue(idLocalPool.get().getReleasedIds().getAvailableIdCount() == 0);
465     }
466
467     @Test
468     public void testMultithreadedIdAllocationForSameKeyFromAvailableIds() throws Exception {
469         setupMockForMultiThreads(false);
470         int numberOfTasks = 3;
471         CountDownLatch latch = new CountDownLatch(numberOfTasks);
472         Set<Long> idSet = new CopyOnWriteArraySet<>();
473         requestIdsConcurrently(latch, numberOfTasks, idSet, true);
474         latch.await();
475         assertTrue(idSet.size() == 1);
476         waitUntilJobIsDone();
477         DataObject dataObject = configDataStore.get(localPoolIdentifier);
478         if (dataObject instanceof IdPool) {
479             IdPool pool = (IdPool) dataObject;
480             assertTrue(idStart == pool.getAvailableIdsHolder().getCursor());
481         }
482     }
483
484     @Test
485     public void testMultithreadedIdAllocationForSameKeyFromReleasedIds() throws Exception {
486         setupMockForMultiThreads(true);
487         int numberOfTasks = 3;
488         CountDownLatch latch = new CountDownLatch(numberOfTasks);
489         Set<Long> idSet = new CopyOnWriteArraySet<>();
490         requestIdsConcurrently(latch, numberOfTasks, idSet, true);
491         latch.await();
492         assertTrue(idSet.size() == 1);
493         waitUntilJobIsDone();
494         DataObject dataObject = configDataStore.get(localPoolIdentifier);
495         if (dataObject instanceof IdPool) {
496             IdPool pool = (IdPool) dataObject;
497             assertTrue(pool.getReleasedIdsHolder().getAvailableIdCount() == 2);
498         }
499     }
500
501     private void setupMockForMultiThreads(boolean isRelease) throws ReadFailedException, UnknownHostException {
502         List<IdPool> listOfIdPool = new ArrayList<>();
503         IdPoolBuilder localIdPool =
504                 buildLocalIdPool(blockSize, idStart, idStart + blockSize, idStart - 1,
505                         localPoolName, poolName);
506         int poolSize = 10;
507         IdPool globalIdPool = buildGlobalIdPool(poolName, idStart, poolSize, blockSize,
508                 buildChildPool(localPoolName)).build();
509         listOfIdPool.add(globalIdPool);
510         setupMocks(listOfIdPool);
511         doReturn(Futures.immediateCheckedFuture(Optional.of(globalIdPool))).when(mockReadTx).read(
512                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
513
514         List<DelayedIdEntries> delayedIdEntries = buildDelayedIdEntries(new long[] {100, 101, 102});
515         ReleasedIdsHolder expectedReleasedIds = createReleasedIdsHolder(3, delayedIdEntries , 0);
516         if (isRelease) {
517             localIdPool.setReleasedIdsHolder(expectedReleasedIds);
518         }
519         listOfIdPool.add(localIdPool.build());
520         listOfIdPool.add(globalIdPool);
521         setupMocks(listOfIdPool);
522         doAnswer(invocation -> {
523             DataObject result = configDataStore.get(idUtils.getIdEntry(parentPoolIdentifier, idKey));
524             if (result == null) {
525                 return Futures.immediateCheckedFuture(Optional.absent());
526             }
527             if (result instanceof IdEntries) {
528                 return Futures.immediateCheckedFuture(Optional.of((IdEntries) result));
529             }
530             return Futures.immediateCheckedFuture(Optional.absent());
531         }).when(mockReadTx).read(LogicalDatastoreType.CONFIGURATION, idUtils.getIdEntry(parentPoolIdentifier, idKey));
532
533         doReturn(Futures.immediateCheckedFuture(Optional.of(globalIdPool))).when(mockReadTx).read(
534                 LogicalDatastoreType.CONFIGURATION, parentPoolIdentifier);
535     }
536
537     private InstanceIdentifier<ReleasedIdsHolder> buildReleaseIdsIdentifier(
538             String poolName) {
539         InstanceIdentifier<ReleasedIdsHolder> releasedIds = InstanceIdentifier
540                 .builder(IdPools.class).child(IdPool.class,
541                         new IdPoolKey(poolName)).child(ReleasedIdsHolder.class).build();
542         return releasedIds;
543     }
544
545     private InstanceIdentifier<ChildPools> buildChildPoolInstanceIdentifier(String poolName, String childPoolName) {
546         InstanceIdentifier<ChildPools> childPool = InstanceIdentifier
547                 .builder(IdPools.class).child(IdPool.class,
548                         new IdPoolKey(poolName)).child(ChildPools.class, new ChildPoolsKey(childPoolName)).build();
549         return childPool;
550     }
551
552     private ReleaseIdInput createReleaseIdInput(String poolName, String idKey) {
553         return new ReleaseIdInputBuilder().setIdKey(idKey).setPoolName(poolName).build();
554     }
555
556     private IdEntries buildIdEntry(String idKey, List<Long> idValuesList) {
557         return new IdEntriesBuilder().setIdKey(idKey).setIdValue(idValuesList).build();
558     }
559
560     private InstanceIdentifier<IdEntries> buildIdEntriesIdentifier(InstanceIdentifier<IdPool> identifier,
561             String idKey) {
562         InstanceIdentifier.InstanceIdentifierBuilder<IdEntries> idEntriesBuilder = identifier
563                 .builder().child(IdEntries.class, new IdEntriesKey(idKey));
564         InstanceIdentifier<IdEntries> idEntry = idEntriesBuilder.build();
565         return idEntry;
566     }
567
568     private CreateIdPoolInput buildCreateIdPool(String poolName, long low, long high) {
569         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder().setPoolName(poolName)
570                 .setLow(low)
571                 .setHigh(high)
572                 .build();
573         return createPool;
574     }
575
576     private IdPoolBuilder buildGlobalIdPool(String poolName, long idStart, long poolSize, int blockSize,
577             List<ChildPools> childPools) {
578         AvailableIdsHolder availableIdsHolder = createAvailableIdsHolder(idStart, poolSize, idStart - 1);
579         ReleasedIdsHolder releasedIdsHolder = createReleasedIdsHolder(0, null, 0);
580         return new IdPoolBuilder().setKey(new IdPoolKey(poolName))
581                 .setPoolName(poolName)
582                 .setBlockSize(blockSize)
583                 .setChildPools(childPools)
584                 .setAvailableIdsHolder(availableIdsHolder)
585                 .setReleasedIdsHolder(releasedIdsHolder);
586     }
587
588     private IdPoolBuilder buildLocalIdPool(int blockSize, int start, int end, int cursor, String localPoolName,
589             String parentPoolName) {
590         ReleasedIdsHolder releasedIdsHolder = createReleasedIdsHolder(0, null, 30);
591         return new IdPoolBuilder().setBlockSize(blockSize)
592                 .setKey(new IdPoolKey(localPoolName))
593                 .setParentPoolName(parentPoolName)
594                 .setReleasedIdsHolder(releasedIdsHolder)
595                 .setAvailableIdsHolder(createAvailableIdsHolder(start, end, cursor));
596     }
597
598     private AllocateIdInput buildAllocateId(String poolName, String idKey) {
599         AllocateIdInput getIdInput = new AllocateIdInputBuilder().setPoolName(poolName)
600                 .setIdKey(idKey).build();
601         return getIdInput;
602     }
603
604     private InstanceIdentifier<IdPool> buildInstanceIdentifier(String poolName) {
605         InstanceIdentifier.InstanceIdentifierBuilder<IdPool> idBuilder =
606                 InstanceIdentifier.builder(IdPools.class).child(IdPool.class, new IdPoolKey(poolName));
607         InstanceIdentifier<IdPool> id = idBuilder.build();
608         return id;
609     }
610
611     private AvailableIdsHolder createAvailableIdsHolder(long low, long high, long cursor) {
612         AvailableIdsHolder availableIdsHolder = new AvailableIdsHolderBuilder()
613                 .setStart(low).setEnd(high).setCursor(cursor).build();
614         return availableIdsHolder;
615     }
616
617     private ReleasedIdsHolder createReleasedIdsHolder(long availableIdCount, List<DelayedIdEntries> delayedIdEntries,
618             long delayTime) {
619         ReleasedIdsHolder releasedIdsHolder = new ReleasedIdsHolderBuilder()
620                 .setAvailableIdCount(availableIdCount)
621                 .setDelayedIdEntries(delayedIdEntries)
622                 .setDelayedTimeSec(delayTime).build();
623         return releasedIdsHolder;
624     }
625
626     private DeleteIdPoolInput createDeleteIdPoolInput(String poolName) {
627         return new DeleteIdPoolInputBuilder().setPoolName(poolName).build();
628     }
629
630     private List<DelayedIdEntries> buildDelayedIdEntries(long[] idValues) {
631         List<DelayedIdEntries> delayedIdEntriesList = new ArrayList<>();
632         for (long idValue : idValues) {
633             DelayedIdEntries delayedIdEntries = new DelayedIdEntriesBuilder().setId(idValue)
634                     .setReadyTimeSec(0L).build();
635             delayedIdEntriesList.add(delayedIdEntries);
636         }
637         return delayedIdEntriesList;
638     }
639
640     private List<ChildPools> buildChildPool(String childPoolName) {
641         ChildPools childPools = new ChildPoolsBuilder().setChildPoolName(childPoolName)
642                 .setLastAccessTime(System.currentTimeMillis() / 1000).build();
643         List<ChildPools> childPoolsList = new ArrayList<>();
644         childPoolsList.add(childPools);
645         return childPoolsList;
646     }
647
648     private void requestIdsConcurrently(CountDownLatch latch, int numberOfTasks, Set<Long> idSet, boolean isSameKey) {
649         ExecutorService executor = Executors.newCachedThreadPool();
650         for (int i = 0; i < numberOfTasks; i++) {
651             executor.execute(new Runnable() {
652                 @Override
653                 public void run() {
654                     Future<RpcResult<AllocateIdOutput>> result;
655                     if (!isSameKey) {
656                         result = idManager.allocateId(buildAllocateId(poolName,
657                                 Thread.currentThread().getName()));
658                     } else {
659                         result = idManager.allocateId(buildAllocateId(poolName,
660                                 idKey));
661                     }
662                     try {
663                         if (result.get().isSuccessful()) {
664                             Long idValue = result.get().getResult().getIdValue();
665                             assertTrue(idValue <= idStart + blockSize);
666                             if (isSameKey) {
667                                 idSet.add(idValue);
668                             } else {
669                                 assertTrue(idSet.add(idValue));
670                             }
671                         } else {
672                             RpcError error = result.get().getErrors().iterator().next();
673                             assertTrue(error.getCause().getMessage().contains("Ids exhausted for pool : " + poolName));
674                         }
675                     } catch (ExecutionException | InterruptedException e) {
676                         assertTrue(e.getCause().getMessage(), false);
677                     } finally {
678                         latch.countDown();
679                     }
680                 }
681             });
682         }
683     }
684
685     private void waitUntilJobIsDone() throws InterruptedException {
686         TimeUnit.SECONDS.sleep(1);
687     }
688 }