f3251e38477c710b48fcf72e40a6846fd93e563d
[vpnservice.git] / idmanager / idmanager-impl / src / main / java / org / opendaylight / idmanager / IdManager.java
1 /*
2  * Copyright (c) 2015 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.idmanager;
10
11 import java.util.Collections;
12 import java.util.Comparator;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.NoSuchElementException;
16 import java.util.concurrent.Future;
17
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutputBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.DeleteIdPoolInput;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdPools;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.IdPool;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.IdPoolBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.IdPoolKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.id.pool.AvailableIdsHolder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.id.pool.AvailableIdsHolderBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.id.pool.ChildPools;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.id.pool.IdEntries;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.id.pool.ReleasedIdsHolder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.id.pool.ReleasedIdsHolderBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.released.ids.DelayedIdEntries;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.released.ids.DelayedIdEntriesBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.lockmanager.rev150819.LockManagerService;
42 import org.opendaylight.yangtools.concepts.ListenerRegistration;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
45 import org.opendaylight.yangtools.yang.common.RpcResult;
46 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import com.google.common.base.Optional;
51 import com.google.common.util.concurrent.Futures;
52
53 public class IdManager implements IdManagerService, AutoCloseable{
54     private static final Logger LOG = LoggerFactory.getLogger(IdManager.class);
55
56     private static final long DEFAULT_IDLE_TIME = 24 * 60 * 60;
57
58     private ListenerRegistration<DataChangeListener> listenerRegistration;
59     private final DataBroker broker;
60     private LockManagerService lockManager;
61
62     @Override
63     public void close() throws Exception {
64         if (listenerRegistration != null) {
65             try {
66                 listenerRegistration.close();
67             } catch (final Exception e) {
68                 LOG.error("Error when cleaning up DataChangeListener.", e);
69             }
70             listenerRegistration = null;
71         }
72         LOG.info("IDManager Closed");
73     }
74
75     public IdManager(final DataBroker db) {
76         broker = db;
77     }
78
79     public void setLockManager(LockManagerService lockManager) {
80         this.lockManager = lockManager;
81     }
82
83     @Override
84     public Future<RpcResult<Void>> createIdPool(CreateIdPoolInput input) {
85         LOG.debug("createIdPool called with input {}", input);
86         String poolName = input.getPoolName();
87         long low = input.getLow();
88         long high = input.getHigh();
89         long blockSize = IdUtils.computeBlockSize(low, high);
90         RpcResultBuilder<Void> createIdPoolRpcBuilder;
91         IdUtils.lockPool(lockManager, poolName);
92         try {
93             InstanceIdentifier<IdPool> idPoolInstanceIdentifier = IdUtils.getIdPoolInstance(poolName);
94             poolName = poolName.intern();
95             IdPool idPool;
96             idPool = createGlobalPool(poolName, low, high, blockSize, idPoolInstanceIdentifier);
97             String localPoolName = IdUtils.getLocalPoolName(poolName);
98             if (createLocalPool(localPoolName, idPool)) {
99                 LOG.debug("Updating global id pool {} with childPool {}", poolName, localPoolName);
100                 updateChildPool(poolName, localPoolName);
101             }
102             createIdPoolRpcBuilder = RpcResultBuilder.success();
103         } catch (Exception ex) {
104             LOG.error("Creation of Id Pool {} failed due to {}", poolName, ex);
105             createIdPoolRpcBuilder = RpcResultBuilder.failed();
106             createIdPoolRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
107         } finally {
108             IdUtils.unlockPool(lockManager, poolName);
109         }
110         return Futures.immediateFuture(createIdPoolRpcBuilder.build());
111     }
112
113     @Override
114     public Future<RpcResult<AllocateIdOutput>> allocateId(AllocateIdInput input) {
115         LOG.debug("AllocateId called with input {}", input);
116         String idKey = input.getIdKey();
117         String poolName = input.getPoolName();
118         String localPoolName = IdUtils.getLocalPoolName(poolName);
119         RpcResultBuilder<AllocateIdOutput> allocateIdRpcBuilder;
120         long newIdValue = -1;
121         AllocateIdOutputBuilder output = new AllocateIdOutputBuilder();
122         try {
123             newIdValue = allocateIdFromLocalPool(poolName, localPoolName, idKey);
124             output.setIdValue(newIdValue);
125             allocateIdRpcBuilder = RpcResultBuilder.success();
126             allocateIdRpcBuilder.withResult(output.build());
127         } catch (Exception ex) {
128             LOG.error("Allocate id in pool {} failed due to {}", poolName, ex);
129             allocateIdRpcBuilder = RpcResultBuilder.failed();
130             allocateIdRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
131         }
132         return Futures.immediateFuture(allocateIdRpcBuilder.build());
133     }
134
135     @Override
136     public Future<RpcResult<Void>> deleteIdPool(DeleteIdPoolInput input) {
137         LOG.debug("DeleteIdPool called with input {}", input);
138         String poolName = input.getPoolName();
139         RpcResultBuilder<Void> deleteIdPoolRpcBuilder;
140         try {
141             InstanceIdentifier<IdPool> idPoolToBeDeleted = IdUtils.getIdPoolInstance(poolName);
142             poolName = poolName.intern();
143             synchronized(poolName) {
144                 IdPool idPool = getIdPool(idPoolToBeDeleted);
145                 List<ChildPools> childPoolList = idPool.getChildPools();
146                 if (childPoolList != null) {
147                     for (ChildPools childPoolName : childPoolList) {
148                         deletePool(childPoolName.getChildPoolName());
149                     }
150                 }
151                 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, idPoolToBeDeleted);
152                 LOG.debug("Deleted id pool {}", poolName);
153             }
154             deleteIdPoolRpcBuilder = RpcResultBuilder.success();
155         }
156         catch (Exception ex) {
157             LOG.error("Delete id in pool {} failed due to {}", poolName, ex);
158             deleteIdPoolRpcBuilder = RpcResultBuilder.failed();
159             deleteIdPoolRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
160         }
161         return Futures.immediateFuture(deleteIdPoolRpcBuilder.build());
162     }
163
164     @Override
165     public Future<RpcResult<Void>> releaseId(ReleaseIdInput input) {
166         String poolName = input.getPoolName();
167         String idKey = input.getIdKey();
168         RpcResultBuilder<Void> releaseIdRpcBuilder;
169         try {
170             releaseIdFromLocalPool(IdUtils.getLocalPoolName(poolName), idKey);
171             releaseIdRpcBuilder = RpcResultBuilder.success();
172         } catch (Exception ex) {
173             LOG.error("Release id {} from pool {} failed due to {}", idKey, poolName, ex);
174             releaseIdRpcBuilder = RpcResultBuilder.failed();
175             releaseIdRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
176         }
177         return Futures.immediateFuture(releaseIdRpcBuilder.build());
178     }
179
180     private long allocateIdFromLocalPool(String parentPoolName, String localPoolName, String idKey) {
181         LOG.trace("Allocating id from local pool {}. Parent pool {}. Idkey {}", localPoolName, parentPoolName, idKey);
182         long newIdValue = -1;
183         InstanceIdentifier<IdPool> localIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(localPoolName);
184         localPoolName = localPoolName.intern();
185         synchronized (localPoolName) {
186             InstanceIdentifier<IdPool> parentIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(parentPoolName);
187             IdPool parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
188             IdPool localPool = null;
189             try {
190                 localPool = getIdPool(localIdPoolInstanceIdentifier);
191             } catch (NoSuchElementException e){
192                 LOG.trace("Creating local pool {} since it was not present", localPoolName);
193                 localPool = IdUtils.createLocalIdPool(localPoolName, parentIdPool);
194                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, localIdPoolInstanceIdentifier, localPool);
195                 IdUtils.lockPool(lockManager, parentPoolName);
196                 try {
197                     updateChildPool(parentPoolName, localPoolName);
198                 } finally {
199                     IdUtils.unlockPool(lockManager, parentPoolName);
200                 }
201                 LOG.debug("Updating global id pool {} with childPool {}", parentPoolName, localPoolName);
202             }
203             IdEntries newIdEntry;
204             List<IdEntries> idEntries = parentIdPool.getIdEntries();
205             AvailableIdsHolderBuilder availableIds = IdUtils.getAvailableIdsHolderBuilder(localPool);
206             ReleasedIdsHolderBuilder releasedIds = IdUtils.getReleaseIdsHolderBuilder(localPool);
207             //Calling cleanupExcessIds since there could be excessive ids.
208             cleanupExcessIds(availableIds, releasedIds, parentPoolName, localPool.getBlockSize());
209             if (idEntries == null) {
210                 idEntries = new LinkedList<IdEntries>();
211             } else {
212                 InstanceIdentifier<IdEntries> existingId = IdUtils.getIdEntry(parentIdPoolInstanceIdentifier, idKey);
213                 Optional<IdEntries> existingIdEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, existingId);
214                 if (existingIdEntry.isPresent()) {
215                     newIdValue = existingIdEntry.get().getIdValue();
216                     LOG.debug("Existing id {} for the key {} ", idKey, newIdValue);
217                     InstanceIdentifier<ReleasedIdsHolder> releasedIdsHolderInstanceIdentifier = InstanceIdentifier
218                             .builder(IdPools.class).child(IdPool.class, new IdPoolKey(localPoolName)).child(ReleasedIdsHolder.class).build();
219                     MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, releasedIdsHolderInstanceIdentifier, releasedIds.build());
220                     return newIdValue;
221                 }
222             }
223             newIdValue = getIdFromPool(localPool, availableIds, releasedIds);
224             LOG.debug("The newIdValue {} for the idKey {}", newIdValue, idKey);
225             newIdEntry = IdUtils.createIdEntries(idKey, newIdValue);
226             idEntries.add(newIdEntry);
227             LOG.debug("The availablelIds are {}", availableIds.build());
228             localPool = new IdPoolBuilder(localPool).setAvailableIdsHolder(availableIds.build())
229                     .setReleasedIdsHolder(releasedIds.build()).build();
230             MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, localIdPoolInstanceIdentifier, localPool);
231             updateChildPool(localPool.getParentPoolName(), localPoolName);
232             //Updating id entries in the parent pool. This will be used for restart scenario
233             IdUtils.lockPool(lockManager, parentPoolName);
234             try {
235                 parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
236                 IdPool parentPool = new IdPoolBuilder(parentIdPool).setIdEntries(idEntries).build();
237                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, parentIdPoolInstanceIdentifier, parentPool);
238             } catch (Exception ex) {
239                 LOG.error("Saving of Id entries to parent pool {} failed due to {}", parentPoolName, ex);
240             } finally {
241                 IdUtils.unlockPool(lockManager, parentPoolName);
242             }
243         }
244         return newIdValue;
245     }
246
247     private long getIdFromPool(IdPool pool, AvailableIdsHolderBuilder availableIds, ReleasedIdsHolderBuilder releasedIds) {
248         long newIdValue = -1;
249         while (true) {
250             newIdValue = IdUtils.getIdFromReleaseIdsIfAvailable(releasedIds);
251             if (newIdValue != -1) {
252                 LOG.debug("Retrieved id value {} from released id holder", newIdValue);
253                 return newIdValue;
254             }
255             newIdValue = IdUtils.getIdFromAvailableIds(availableIds);
256             if (newIdValue != -1) {
257                 LOG.debug("Creating a new id {} for the pool: {} ", newIdValue, pool.getPoolName());
258                 return newIdValue;
259             }
260             long idCount = allocateIdBlockFromParentPool(pool.getParentPoolName(), availableIds, releasedIds);
261             if (idCount <= 0) {
262                 LOG.debug("Unable to allocate Id block from global pool");
263                 throw new RuntimeException(String.format("Ids exhausted for pool : %s", pool.getPoolName()));
264             }
265         }
266     }
267
268     /**
269      * Changes made to releaseIds and AvailableIds are not persisted.
270      * @param availableIds
271      * @param releasedIds
272      * @param parentPoolName
273      * @param blockSize
274      */
275     private void cleanupExcessIds(AvailableIdsHolderBuilder availableIds, ReleasedIdsHolderBuilder releasedIds, String parentPoolName, int blockSize) {
276         IdUtils.processDelayList(releasedIds);
277         long totalAvailableIdCount = releasedIds.getAvailableIdCount() + IdUtils.getAvailableIdsCount(availableIds);
278         if (totalAvailableIdCount > blockSize * 2) {
279             parentPoolName = parentPoolName.intern();
280             InstanceIdentifier<ReleasedIdsHolder> releasedIdInstanceIdentifier = IdUtils.getReleasedIdsHolderInstance(parentPoolName);
281             IdUtils.lockPool(lockManager, parentPoolName);
282             try {
283                 Optional<ReleasedIdsHolder> releasedIdsHolder = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, releasedIdInstanceIdentifier);
284                 ReleasedIdsHolderBuilder releasedIdsParent;
285                 if (!releasedIdsHolder.isPresent()) {
286                     LOG.error("ReleasedIds not present in parent pool. Unable to cleanup excess ids");
287                     return;
288                 }
289                 releasedIdsParent = new ReleasedIdsHolderBuilder(releasedIdsHolder.get());
290                 LOG.debug("Releasing excesss Ids from local pool");
291                 IdUtils.freeExcessAvailableIds(releasedIds, releasedIdsParent, blockSize);
292                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, releasedIdInstanceIdentifier, releasedIdsParent.build());
293             } finally {
294                 IdUtils.unlockPool(lockManager, parentPoolName);
295             }
296         }
297     }
298
299     /**
300      * Changes made to availableIds and releasedIds will not be persisted to the datastore
301      * @param parentPoolName
302      * @param availableIdsBuilder
303      * @param releasedIdsBuilder
304      * @return
305      */
306     private long allocateIdBlockFromParentPool(String parentPoolName,
307             AvailableIdsHolderBuilder availableIdsBuilder, ReleasedIdsHolderBuilder releasedIdsBuilder) {
308         LOG.debug("Allocating block of id from parent pool {}", parentPoolName);
309         InstanceIdentifier<IdPool> idPoolInstanceIdentifier = IdUtils.getIdPoolInstance(parentPoolName);
310         parentPoolName = parentPoolName.intern();
311         long idCount = -1;
312         IdUtils.lockPool(lockManager, parentPoolName);
313         try {
314             IdPool parentIdPool = getIdPool(idPoolInstanceIdentifier);
315             ReleasedIdsHolderBuilder releasedIdsBuilderParent = IdUtils.getReleaseIdsHolderBuilder(parentIdPool);
316             while (true) {
317                 idCount = allocateIdBlockFromReleasedIdsHolder(releasedIdsBuilder, releasedIdsBuilderParent, parentIdPool);
318                 if (idCount > 0) {
319                     return idCount;
320                 }
321                 idCount = allocateIdBlockFromAvailableIdsHolder(availableIdsBuilder, parentIdPool);
322                 if (idCount > 0) {
323                     return idCount;
324                 }
325                 idCount = getIdsFromOtherChildPools(releasedIdsBuilderParent, parentIdPool);
326                 if (idCount <= 0) {
327                     LOG.debug("Unable to allocate Id block from global pool");
328                     throw new RuntimeException(String.format("Ids exhausted for pool : %s", parentPoolName));
329                 }
330             }
331         }
332         finally {
333             IdUtils.unlockPool(lockManager, parentPoolName);
334         }
335     }
336
337     private long getIdsFromOtherChildPools(ReleasedIdsHolderBuilder releasedIdsBuilderParent, IdPool parentIdPool) {
338         List<ChildPools> childPoolsList = parentIdPool.getChildPools();
339         // Sorting the child pools on last accessed time so that the pool that was not accessed for a long time comes first.
340         Collections.sort(childPoolsList, new Comparator<ChildPools>() {
341             @Override
342             public int compare(ChildPools childPool1, ChildPools childPool2) {
343                 return childPool1.getLastAccessTime().compareTo(childPool2.getLastAccessTime());
344             }
345         });
346         long currentTime = System.currentTimeMillis() / 1000;
347         for (ChildPools childPools : childPoolsList) {
348             if (childPools.getLastAccessTime() + DEFAULT_IDLE_TIME < currentTime) {
349                 break;
350             }
351             if (!childPools.getChildPoolName().equals(IdUtils.getLocalPoolName(parentIdPool.getPoolName()))) {
352                 InstanceIdentifier<IdPool> idPoolInstanceIdentifier = IdUtils.getIdPoolInstance(childPools.getChildPoolName());
353                 IdPool otherChildPool = getIdPool(idPoolInstanceIdentifier);
354                 ReleasedIdsHolderBuilder releasedIds = IdUtils.getReleaseIdsHolderBuilder(otherChildPool);
355                 AvailableIdsHolderBuilder availableIds = IdUtils.getAvailableIdsHolderBuilder(otherChildPool);
356                 long totalAvailableIdCount = releasedIds.getDelayedIdEntries().size() + IdUtils.getAvailableIdsCount(availableIds);
357                 List<DelayedIdEntries> delayedIdEntriesChild = releasedIds.getDelayedIdEntries();
358                 List<DelayedIdEntries> delayedIdEntriesParent = releasedIdsBuilderParent.getDelayedIdEntries();
359                 if (delayedIdEntriesParent == null) {
360                     delayedIdEntriesParent = new LinkedList<>();
361                 }
362                 delayedIdEntriesParent.addAll(delayedIdEntriesChild);
363                 delayedIdEntriesChild.removeAll(delayedIdEntriesChild);
364                 while (IdUtils.isIdAvailable(availableIds)) {
365                     long cursor = availableIds.getCursor() + 1;
366                     delayedIdEntriesParent.add(new DelayedIdEntriesBuilder().setId(cursor).setReadyTimeSec(System.currentTimeMillis()).build());
367                     availableIds.setCursor(cursor);
368                 }
369                 long count = releasedIdsBuilderParent.getAvailableIdCount() + totalAvailableIdCount;
370                 releasedIdsBuilderParent.setDelayedIdEntries(delayedIdEntriesParent).setAvailableIdCount(count);
371                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier,
372                         new IdPoolBuilder(otherChildPool).setAvailableIdsHolder(availableIds.build()).setReleasedIdsHolder(releasedIds.build()).build());
373                 return totalAvailableIdCount;
374             }
375         }
376         return 0;
377     }
378
379     private long allocateIdBlockFromReleasedIdsHolder(ReleasedIdsHolderBuilder releasedIdsBuilderChild, ReleasedIdsHolderBuilder releasedIdsBuilderParent, IdPool parentIdPool) {
380         if (releasedIdsBuilderParent.getAvailableIdCount() == 0) {
381             LOG.debug("Ids unavailable in releasedIds of parent pool {}", parentIdPool);
382             return 0;
383         }
384         List<DelayedIdEntries> delayedIdEntriesParent = releasedIdsBuilderParent.getDelayedIdEntries();
385         List<DelayedIdEntries> delayedIdEntriesChild = releasedIdsBuilderChild.getDelayedIdEntries();
386         if (delayedIdEntriesChild == null) {
387             delayedIdEntriesChild = new LinkedList<DelayedIdEntries>();
388         }
389         int idCount = Math.min(delayedIdEntriesParent.size(), parentIdPool.getBlockSize());
390         List<DelayedIdEntries> idEntriesToBeRemoved = delayedIdEntriesParent.subList(0, idCount);
391         delayedIdEntriesChild.addAll(0, idEntriesToBeRemoved);
392         delayedIdEntriesParent.removeAll(idEntriesToBeRemoved);
393         releasedIdsBuilderParent.setDelayedIdEntries(delayedIdEntriesParent);
394         releasedIdsBuilderChild.setDelayedIdEntries(delayedIdEntriesChild);
395         releasedIdsBuilderChild.setAvailableIdCount(releasedIdsBuilderChild.getAvailableIdCount() + idCount);
396         InstanceIdentifier<ReleasedIdsHolder> releasedIdsHolderInstanceIdentifier = InstanceIdentifier
397                 .builder(IdPools.class).child(IdPool.class,
398                         new IdPoolKey(parentIdPool.getPoolName())).child(ReleasedIdsHolder.class).build();
399         releasedIdsBuilderParent.setAvailableIdCount(releasedIdsBuilderParent.getAvailableIdCount() - idCount);
400         LOG.debug("Allocated {} ids from releasedIds of parent pool {}", idCount, parentIdPool);
401         MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, releasedIdsHolderInstanceIdentifier, releasedIdsBuilderParent.build());
402         return idCount;
403     }
404
405     private long allocateIdBlockFromAvailableIdsHolder(AvailableIdsHolderBuilder availableIdsBuilder, IdPool parentIdPool) {
406         long idCount = 0;
407         AvailableIdsHolderBuilder availableIdsBuilderParent = IdUtils.getAvailableIdsHolderBuilder(parentIdPool);
408         long end = availableIdsBuilderParent.getEnd();
409         long cur = availableIdsBuilderParent.getCursor();
410         if (!IdUtils.isIdAvailable(availableIdsBuilderParent)) {
411             LOG.debug("Ids exhausted in parent pool {}", parentIdPool);
412             return idCount;
413         }
414         // Update availableIdsHolder of Local Pool
415         availableIdsBuilder.setStart(cur + 1);
416         idCount = Math.min(end - cur, parentIdPool.getBlockSize());
417         availableIdsBuilder.setEnd(cur + idCount);
418         availableIdsBuilder.setCursor(cur);
419         // Update availableIdsHolder of Global Pool
420         InstanceIdentifier<AvailableIdsHolder> availableIdsHolderInstanceIdentifier = InstanceIdentifier
421                 .builder(IdPools.class).child(IdPool.class,
422                         new IdPoolKey(parentIdPool.getPoolName())).child(AvailableIdsHolder.class).build();
423         availableIdsBuilderParent.setCursor(cur + idCount);
424         LOG.debug("Allocated {} ids from availableIds of global pool {}", idCount, parentIdPool);
425         MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, availableIdsHolderInstanceIdentifier, availableIdsBuilderParent.build());
426         return idCount;
427     }
428
429     private void releaseIdFromLocalPool(String poolName, String idKey) {
430         InstanceIdentifier<IdPool> localIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(poolName);
431         poolName = poolName.intern();
432         synchronized (poolName) {
433             IdPool localPool = getIdPool(localIdPoolInstanceIdentifier);
434             String parentPoolName = localPool.getParentPoolName();
435             InstanceIdentifier<IdPool> parentIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(parentPoolName);
436             IdPool parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
437             List<IdEntries> idEntries = parentIdPool.getIdEntries();
438             List<IdEntries> newIdEntries = idEntries;
439             if (idEntries == null) {
440                 throw new RuntimeException("Id Entries does not exist");
441             }
442             InstanceIdentifier<IdEntries> existingId = IdUtils.getIdEntry(parentIdPoolInstanceIdentifier, idKey);
443             Optional<IdEntries> existingIdEntryObject = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, existingId);
444             if (!existingIdEntryObject.isPresent()) {
445                 throw new RuntimeException(String.format("Specified Id key %s does not exist in id pool %s", idKey, poolName));
446             }
447             IdEntries existingIdEntry = existingIdEntryObject.get();
448             long idValue = existingIdEntry.getIdValue();
449             boolean isRemoved = newIdEntries.remove(existingIdEntry);
450             LOG.debug("The entry {} is removed {}", existingIdEntry, isRemoved);
451             ReleasedIdsHolderBuilder releasedIds = IdUtils.getReleaseIdsHolderBuilder(localPool);
452             AvailableIdsHolderBuilder availableIds = IdUtils.getAvailableIdsHolderBuilder(localPool);
453             long delayTime = System.currentTimeMillis() / 1000 + releasedIds.getDelayedTimeSec();
454             DelayedIdEntries delayedIdEntry = IdUtils.createDelayedIdEntry(idValue, delayTime);
455             List<DelayedIdEntries> delayedIdEntries = releasedIds.getDelayedIdEntries();
456             if (delayedIdEntries == null) {
457                 delayedIdEntries = new LinkedList<DelayedIdEntries>();
458             }
459             delayedIdEntries.add(delayedIdEntry);
460             long availableIdCount = releasedIds
461                     .getAvailableIdCount() == null ? 0
462                             : releasedIds.getAvailableIdCount();
463             releasedIds.setDelayedIdEntries(delayedIdEntries);
464             releasedIds.setAvailableIdCount(availableIdCount);
465             //Calling cleanupExcessIds since there could be excessive ids.
466             cleanupExcessIds(availableIds, releasedIds, parentPoolName, localPool.getBlockSize());
467             localPool = new IdPoolBuilder(localPool)
468                     .setAvailableIdsHolder(availableIds.build())
469                     .setReleasedIdsHolder(releasedIds.build()).build();
470             MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, localIdPoolInstanceIdentifier, localPool);
471             LOG.debug("Released id ({}, {}) from pool {}", idKey, idValue, poolName);
472             //Updating id entries in the parent pool. This will be used for restart scenario
473             IdUtils.lockPool(lockManager, parentPoolName);
474             try {
475                 parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
476                 IdPool parentPool = new IdPoolBuilder(parentIdPool).setIdEntries(newIdEntries).build();
477                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, parentIdPoolInstanceIdentifier, parentPool);
478             } catch (Exception ex) {
479                 LOG.error("Saving of Id entries to parent pool {} failed due to {}", parentPoolName, ex);
480             } finally {
481                 IdUtils.unlockPool(lockManager, parentPoolName);
482             }
483         }
484     }
485
486     private IdPool createGlobalPool(String poolName, long low, long high,
487             long blockSize, InstanceIdentifier<IdPool> idPoolInstanceIdentifier) {
488         IdPool idPool;
489         Optional<IdPool> existingIdPool = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier);
490         if (!existingIdPool.isPresent()) {
491             LOG.debug("Creating new global pool {}", poolName);
492             idPool = IdUtils.createGlobalPool(poolName, low, high, blockSize);
493             MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier, idPool);
494         }
495         else {
496             idPool = existingIdPool.get();
497             LOG.debug("GlobalPool exists {}", idPool);
498         }
499         return idPool;
500     }
501
502     private boolean createLocalPool(String localPoolName, IdPool idPool) {
503         localPoolName = localPoolName.intern();
504         synchronized (localPoolName) {
505             InstanceIdentifier<IdPool> localIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(localPoolName);
506             Optional<IdPool> localIdPool = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, localIdPoolInstanceIdentifier);
507             if (!localIdPool.isPresent()) {
508                 LOG.debug("Creating new local pool");
509                 IdPool newLocalIdPool = IdUtils.createLocalIdPool(localPoolName, idPool);
510                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, localIdPoolInstanceIdentifier, newLocalIdPool);
511                 LOG.debug("Local pool created {}", newLocalIdPool);
512                 return true;
513             }
514         }
515         return false;
516     }
517
518     private void deletePool(String poolName) {
519         InstanceIdentifier<IdPool> idPoolToBeDeleted = IdUtils.getIdPoolInstance(poolName);
520         synchronized (poolName) {
521             Optional<IdPool> idPool = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, idPoolToBeDeleted);
522             if (idPool.isPresent()) {
523                 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, idPoolToBeDeleted);
524                 LOG.debug("Deleted local pool {}", poolName);
525             }
526         }
527     }
528
529     private IdPool getIdPool(InstanceIdentifier<IdPool> idPoolInstanceIdentifier) {
530         Optional<IdPool> idPool = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier);
531         if (!idPool.isPresent()) {
532             throw new NoSuchElementException(String.format("Specified pool %s does not exist" , idPool));
533         }
534         LOG.trace("GetIdPool : Read id pool {} ", idPool);
535         return idPool.get();
536     }
537
538     private void updateChildPool(String poolName, String localPoolName) {
539         ChildPools childPool = IdUtils.createChildPool(localPoolName);
540         InstanceIdentifier<ChildPools> childPoolInstanceIdentifier = IdUtils.getChildPoolsInstanceIdentifier(poolName, localPoolName);
541         MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, childPoolInstanceIdentifier, childPool);
542     }
543 }