Handle nullable lists
[genius.git] / idmanager / idmanager-impl / src / main / java / org / opendaylight / genius / idmanager / IdUtils.java
1 /*
2  * Copyright (c) 2016, 2017 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
9 package org.opendaylight.genius.idmanager;
10
11 import static org.opendaylight.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
12
13 import com.google.common.base.Optional;
14 import com.google.common.net.InetAddresses;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.concurrent.CompletableFuture;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.atomic.AtomicInteger;
26 import javax.annotation.Nonnull;
27 import javax.annotation.Nullable;
28 import javax.inject.Singleton;
29 import org.opendaylight.genius.idmanager.ReleasedIdHolder.DelayedIdEntry;
30 import org.opendaylight.genius.infra.Datastore.Configuration;
31 import org.opendaylight.genius.infra.TypedWriteTransaction;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdPools;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPool;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.AvailableIdsHolder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.AvailableIdsHolderBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPools;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPoolsBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPoolsKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntries;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntriesBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntriesKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ReleasedIdsHolder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ReleasedIdsHolderBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.released.ids.DelayedIdEntries;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.released.ids.DelayedIdEntriesBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockOutput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockInput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockInputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.UnlockOutput;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.opendaylight.yangtools.yang.common.RpcResult;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 @Singleton
61 public class IdUtils {
62
63     private static final Logger LOG = LoggerFactory.getLogger(IdUtils.class);
64
65     public static final long DEFAULT_DELAY_TIME = 30;
66     private static final long DEFAULT_AVAILABLE_ID_COUNT = 0;
67     private static final int DEFAULT_BLOCK_SIZE_DIFF = 10;
68     public static final int RETRY_COUNT = 6;
69
70     private final ConcurrentHashMap<String, CompletableFuture<List<Long>>> allocatedIdMap = new ConcurrentHashMap<>();
71     private final ConcurrentHashMap<String, CountDownLatch> releaseIdLatchMap = new ConcurrentHashMap<>();
72     private final ConcurrentHashMap<String, AtomicInteger> poolUpdatedMap = new ConcurrentHashMap<>();
73
74     private final int bladeId;
75
76     public IdUtils() throws UnknownHostException {
77         bladeId = InetAddresses.coerceToInteger(InetAddress.getLocalHost());
78     }
79
80     public CompletableFuture<List<Long>> removeAllocatedIds(String uniqueIdKey) {
81         return allocatedIdMap.remove(uniqueIdKey);
82     }
83
84     public CompletableFuture<List<Long>> putAllocatedIdsIfAbsent(String uniqueIdKey,
85             CompletableFuture<List<Long>> futureIdValues) {
86         return allocatedIdMap.putIfAbsent(uniqueIdKey, futureIdValues);
87     }
88
89     public void putReleaseIdLatch(String uniqueIdKey, CountDownLatch latch) {
90         releaseIdLatchMap.put(uniqueIdKey, latch);
91     }
92
93     public CountDownLatch getReleaseIdLatch(String uniqueIdKey) {
94         return releaseIdLatchMap.get(uniqueIdKey);
95     }
96
97     public CountDownLatch removeReleaseIdLatch(String uniqueIdKey) {
98         return releaseIdLatchMap.remove(uniqueIdKey);
99     }
100
101     public InstanceIdentifier<IdEntries> getIdEntry(InstanceIdentifier<IdPool> poolName, String idKey) {
102         InstanceIdentifier.InstanceIdentifierBuilder<IdEntries> idEntriesBuilder = poolName
103                 .builder().child(IdEntries.class, new IdEntriesKey(idKey));
104         return idEntriesBuilder.build();
105     }
106
107     public IdEntries createIdEntries(String idKey, List<Long> newIdVals) {
108         return new IdEntriesBuilder().withKey(new IdEntriesKey(idKey))
109                 .setIdKey(idKey).setIdValue(newIdVals).build();
110     }
111
112     public DelayedIdEntries createDelayedIdEntry(long idValue, long delayTime) {
113         return new DelayedIdEntriesBuilder()
114                 .setId(idValue)
115                 .setReadyTimeSec(delayTime).build();
116     }
117
118     protected IdPool createGlobalPool(String poolName, long low, long high, long blockSize) {
119         AvailableIdsHolder availableIdsHolder = createAvailableIdsHolder(low, high, low - 1);
120         ReleasedIdsHolder releasedIdsHolder = createReleasedIdsHolder(DEFAULT_AVAILABLE_ID_COUNT, 0);
121         int size = (int) blockSize;
122         return new IdPoolBuilder().withKey(new IdPoolKey(poolName))
123                 .setBlockSize(size).setPoolName(poolName)
124                 .setAvailableIdsHolder(availableIdsHolder)
125                 .setReleasedIdsHolder(releasedIdsHolder).build();
126     }
127
128     public AvailableIdsHolder createAvailableIdsHolder(long low, long high, long cursor) {
129         return new AvailableIdsHolderBuilder()
130                 .setStart(low).setEnd(high).setCursor(cursor).build();
131     }
132
133     protected ReleasedIdsHolder createReleasedIdsHolder(long availableIdCount, long delayTime) {
134         return new ReleasedIdsHolderBuilder()
135                 .setAvailableIdCount(availableIdCount)
136                 .setDelayedTimeSec(delayTime).build();
137     }
138
139     public InstanceIdentifier<IdPool> getIdPoolInstance(String poolName) {
140         InstanceIdentifier.InstanceIdentifierBuilder<IdPool> idPoolBuilder = InstanceIdentifier
141                 .builder(IdPools.class).child(IdPool.class,
142                         new IdPoolKey(poolName));
143         return idPoolBuilder.build();
144     }
145
146     public InstanceIdentifier<ReleasedIdsHolder> getReleasedIdsHolderInstance(String poolName) {
147         InstanceIdentifier.InstanceIdentifierBuilder<ReleasedIdsHolder> releasedIdsHolder = InstanceIdentifier
148                 .builder(IdPools.class).child(IdPool.class,
149                         new IdPoolKey(poolName)).child(ReleasedIdsHolder.class);
150         return releasedIdsHolder.build();
151     }
152
153     protected boolean isIdAvailable(AvailableIdsHolderBuilder availableIds) {
154         if (availableIds.getCursor() != null && availableIds.getEnd() != null) {
155             return availableIds.getCursor() < availableIds.getEnd();
156         }
157         return false;
158     }
159
160     // public only to re-use this from IdManagerTest
161     public String getLocalPoolName(String poolName) {
162         return poolName + "." + bladeId;
163     }
164
165     protected ChildPools createChildPool(String childPoolName) {
166         return new ChildPoolsBuilder().withKey(new ChildPoolsKey(childPoolName)).setChildPoolName(childPoolName)
167                 .setLastAccessTime(System.currentTimeMillis() / 1000).build();
168     }
169
170     protected AvailableIdsHolderBuilder getAvailableIdsHolderBuilder(IdPool pool) {
171         AvailableIdsHolder availableIds = pool.getAvailableIdsHolder();
172         if (availableIds != null) {
173             return new AvailableIdsHolderBuilder(availableIds);
174         }
175         return new AvailableIdsHolderBuilder();
176     }
177
178     protected static ReleasedIdsHolderBuilder getReleaseIdsHolderBuilder(IdPool pool) {
179         ReleasedIdsHolder releasedIds = pool.getReleasedIdsHolder();
180         if (releasedIds != null) {
181             return new ReleasedIdsHolderBuilder(releasedIds);
182         }
183         return new ReleasedIdsHolderBuilder();
184     }
185
186     /**
187      * Changes made to the parameters passed are not persisted to the Datastore.
188      * Method invoking should ensure that these gets persisted.
189      */
190     public void freeExcessAvailableIds(ReleasedIdHolder releasedIdHolder,
191         ReleasedIdsHolderBuilder releasedIdsParent, long idCountToBeFreed) {
192         List<DelayedIdEntries> existingDelayedIdEntriesInParent = releasedIdsParent.getDelayedIdEntries();
193         long availableIdCountChild = releasedIdHolder.getAvailableIdCount();
194         if (existingDelayedIdEntriesInParent == null) {
195             existingDelayedIdEntriesInParent = new LinkedList<>();
196         }
197         idCountToBeFreed = Math.min(idCountToBeFreed, availableIdCountChild);
198         for (int index = 0; index < idCountToBeFreed; index++) {
199             Optional<Long> idValueOptional = releasedIdHolder.allocateId();
200             if (!idValueOptional.isPresent()) {
201                 break;
202             }
203             long idValue = idValueOptional.get();
204             DelayedIdEntries delayedIdEntries = new DelayedIdEntriesBuilder().setId(idValue)
205                     .setReadyTimeSec(System.currentTimeMillis() / 1000).build();
206             existingDelayedIdEntriesInParent.add(delayedIdEntries);
207         }
208         long availableIdCountParent = releasedIdsParent.getAvailableIdCount();
209         releasedIdsParent.setDelayedIdEntries(existingDelayedIdEntriesInParent)
210                 .setAvailableIdCount(availableIdCountParent + idCountToBeFreed);
211     }
212
213     public InstanceIdentifier<IdEntries> getIdEntriesInstanceIdentifier(String poolName, String idKey) {
214         return InstanceIdentifier
215                 .builder(IdPools.class).child(IdPool.class,
216                         new IdPoolKey(poolName)).child(IdEntries.class, new IdEntriesKey(idKey)).build();
217     }
218
219     protected InstanceIdentifier<ChildPools> getChildPoolsInstanceIdentifier(String poolName, String localPoolName) {
220         return InstanceIdentifier
221                 .builder(IdPools.class)
222                 .child(IdPool.class, new IdPoolKey(poolName))
223                 .child(ChildPools.class, new ChildPoolsKey(localPoolName)).build();
224     }
225
226     public long computeBlockSize(long low, long high) {
227         long blockSize;
228
229         long diff = high - low;
230         if (diff > DEFAULT_BLOCK_SIZE_DIFF) {
231             blockSize = diff / DEFAULT_BLOCK_SIZE_DIFF;
232         } else {
233             blockSize = 1;
234         }
235         return blockSize;
236     }
237
238     public long getAvailableIdsCount(AvailableIdsHolderBuilder availableIds) {
239         if (availableIds != null && isIdAvailable(availableIds)) {
240             return availableIds.getEnd() - availableIds.getCursor();
241         }
242         return 0;
243     }
244
245     public void lock(LockManagerService lockManager, String poolName) throws IdManagerException {
246         LockInput input = new LockInputBuilder().setLockName(poolName).build();
247         Future<RpcResult<LockOutput>> result = lockManager.lock(input);
248         try {
249             if (result != null && result.get().isSuccessful()) {
250                 if (LOG.isDebugEnabled()) {
251                     LOG.debug("Acquired lock {}", poolName);
252                 }
253             } else {
254                 throw new IdManagerException(String.format("Unable to getLock for pool %s", poolName));
255             }
256         } catch (InterruptedException | ExecutionException e) {
257             LOG.error("Unable to getLock for pool {}", poolName, e);
258             throw new RuntimeException(String.format("Unable to getLock for pool %s", poolName), e);
259         }
260     }
261
262     public void unlock(LockManagerService lockManager, String poolName) {
263         UnlockInput input = new UnlockInputBuilder().setLockName(poolName).build();
264         Future<RpcResult<UnlockOutput>> result = lockManager.unlock(input);
265         try {
266             if (result != null && result.get().isSuccessful()) {
267                 if (LOG.isDebugEnabled()) {
268                     LOG.debug("Unlocked {}", poolName);
269                 }
270             } else {
271                 if (LOG.isDebugEnabled()) {
272                     LOG.debug("Unable to unlock pool {}", poolName);
273                 }
274             }
275         } catch (InterruptedException | ExecutionException e) {
276             LOG.error("Unable to unlock for pool {}", poolName, e);
277             throw new RuntimeException(String.format("Unable to unlock pool %s", poolName), e);
278         }
279     }
280
281     public InstanceIdentifier<IdPools> getIdPools() {
282         return InstanceIdentifier.builder(IdPools.class).build();
283     }
284
285     public void syncReleaseIdHolder(ReleasedIdHolder releasedIdHolder, IdPoolBuilder idPool) {
286         long delayTime = releasedIdHolder.getTimeDelaySec();
287         ReleasedIdsHolderBuilder releasedIdsBuilder = new ReleasedIdsHolderBuilder();
288         List<DelayedIdEntries> delayedIdEntriesList = new ArrayList<>();
289         List<DelayedIdEntry> delayList = releasedIdHolder.getDelayedEntries();
290         for (DelayedIdEntry delayedId : delayList) {
291             DelayedIdEntries delayedIdEntry = createDelayedIdEntry(delayedId.getId(), delayedId.getReadyTimeSec());
292             delayedIdEntriesList.add(delayedIdEntry);
293         }
294         releasedIdsBuilder.setAvailableIdCount((long) delayedIdEntriesList.size()).setDelayedTimeSec(delayTime)
295                 .setDelayedIdEntries(delayedIdEntriesList);
296         idPool.setReleasedIdsHolder(releasedIdsBuilder.build());
297     }
298
299     public void syncAvailableIdHolder(AvailableIdHolder availableIdHolder, IdPoolBuilder idPool) {
300         long cur = availableIdHolder.getCur().get();
301         long low = availableIdHolder.getLow();
302         long high = availableIdHolder.getHigh();
303         AvailableIdsHolder availableIdsHolder = createAvailableIdsHolder(low, high, cur);
304         idPool.setAvailableIdsHolder(availableIdsHolder);
305     }
306
307     public void updateChildPool(TypedWriteTransaction<Configuration> tx, String poolName, String localPoolName) {
308         ChildPools childPool = createChildPool(localPoolName);
309         InstanceIdentifier<ChildPools> childPoolInstanceIdentifier =
310                 getChildPoolsInstanceIdentifier(poolName, localPoolName);
311         tx.merge(childPoolInstanceIdentifier, childPool, CREATE_MISSING_PARENTS);
312     }
313
314     public void incrementPoolUpdatedMap(String localPoolName) {
315         AtomicInteger value = poolUpdatedMap.putIfAbsent(localPoolName, new AtomicInteger(0));
316         if (value == null) {
317             value = poolUpdatedMap.get(localPoolName);
318         }
319         value.incrementAndGet();
320     }
321
322     public void decrementPoolUpdatedMap(String localPoolName) {
323         AtomicInteger value = poolUpdatedMap.get(localPoolName);
324         if (value != null && value.get() >= 1) {
325             value.decrementAndGet();
326         }
327     }
328
329     public boolean getPoolUpdatedMap(String localPoolName) {
330         AtomicInteger value = poolUpdatedMap.get(localPoolName);
331         return value != null && value.get() > 0;
332     }
333
334     public void removeFromPoolUpdatedMap(String localPoolName) {
335         poolUpdatedMap.remove(localPoolName);
336     }
337
338     public String getUniqueKey(String parentPoolName, String idKey) {
339         return parentPoolName + idKey;
340     }
341
342     // TODO Replace this with mdsal's DataObjectUtils.nullToEmpty when upgrading to mdsal 3.0.2
343     @Nonnull
344     public static <T> List<T> nullToEmpty(final @Nullable List<T> input) {
345         return input != null ? input : new ArrayList<>(0);
346     }
347 }