RA: Add capability to assign new numbers for range
[ccsdk/sli/adaptors.git] / resource-assignment / provider / src / main / java / org / onap / ccsdk / sli / adaptors / rm / comp / AllocationFunction.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : SDN-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                         reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.ccsdk.sli.adaptors.rm.comp;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.SortedSet;
31 import java.util.TreeSet;
32 import org.onap.ccsdk.sli.adaptors.lock.comp.LockHelper;
33 import org.onap.ccsdk.sli.adaptors.lock.comp.ResourceLockedException;
34 import org.onap.ccsdk.sli.adaptors.lock.comp.SynchronizedFunction;
35 import org.onap.ccsdk.sli.adaptors.rm.dao.ResourceDao;
36 import org.onap.ccsdk.sli.adaptors.rm.data.AllocationOutcome;
37 import org.onap.ccsdk.sli.adaptors.rm.data.AllocationRequest;
38 import org.onap.ccsdk.sli.adaptors.rm.data.AllocationStatus;
39 import org.onap.ccsdk.sli.adaptors.rm.data.LabelAllocationOutcome;
40 import org.onap.ccsdk.sli.adaptors.rm.data.LabelAllocationRequest;
41 import org.onap.ccsdk.sli.adaptors.rm.data.LabelResource;
42 import org.onap.ccsdk.sli.adaptors.rm.data.LimitAllocationOutcome;
43 import org.onap.ccsdk.sli.adaptors.rm.data.LimitAllocationRequest;
44 import org.onap.ccsdk.sli.adaptors.rm.data.LimitResource;
45 import org.onap.ccsdk.sli.adaptors.rm.data.MultiAssetAllocationOutcome;
46 import org.onap.ccsdk.sli.adaptors.rm.data.MultiAssetAllocationRequest;
47 import org.onap.ccsdk.sli.adaptors.rm.data.MultiResourceAllocationOutcome;
48 import org.onap.ccsdk.sli.adaptors.rm.data.MultiResourceAllocationRequest;
49 import org.onap.ccsdk.sli.adaptors.rm.data.Range;
50 import org.onap.ccsdk.sli.adaptors.rm.data.RangeAllocationOutcome;
51 import org.onap.ccsdk.sli.adaptors.rm.data.RangeAllocationRequest;
52 import org.onap.ccsdk.sli.adaptors.rm.data.RangeResource;
53 import org.onap.ccsdk.sli.adaptors.rm.data.Resource;
54 import org.onap.ccsdk.sli.adaptors.rm.data.ResourceKey;
55 import org.onap.ccsdk.sli.adaptors.rm.data.ResourceType;
56 import org.onap.ccsdk.sli.adaptors.rm.util.LabelUtil;
57 import org.onap.ccsdk.sli.adaptors.rm.util.LimitUtil;
58 import org.onap.ccsdk.sli.adaptors.rm.util.RangeUtil;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 class AllocationFunction extends SynchronizedFunction {
63
64     @SuppressWarnings("unused")
65     private static final Logger log = LoggerFactory.getLogger(AllocationFunction.class);
66
67     private ResourceDao resourceDao;
68
69     private AllocationRequest request;
70     private AllocationOutcome outcome;
71
72     private List<Resource> updateList = new ArrayList<>();
73
74     public AllocationFunction(LockHelper lockHelper, ResourceDao resourceDao, AllocationRequest request,
75             int lockTimeout) {
76         super(lockHelper, getLockNames(request), lockTimeout);
77         this.resourceDao = resourceDao;
78         this.request = request;
79     }
80
81     private static Collection<String> getLockNames(AllocationRequest request) {
82         Set<String> lockResourceNames = new HashSet<>();
83         addLockNames(lockResourceNames, request);
84         return lockResourceNames;
85     }
86
87     private static void addLockNames(Set<String> lockResourceNames, AllocationRequest request) {
88         if (request instanceof MultiAssetAllocationRequest) {
89             MultiAssetAllocationRequest req = (MultiAssetAllocationRequest) request;
90             if (req.assetIdList != null) {
91                 lockResourceNames.addAll(req.assetIdList);
92             }
93         } else if (request instanceof MultiResourceAllocationRequest) {
94             MultiResourceAllocationRequest req = (MultiResourceAllocationRequest) request;
95             if (req.allocationRequestList != null) {
96                 for (AllocationRequest request1 : req.allocationRequestList) {
97                     addLockNames(lockResourceNames, request1);
98                 }
99             }
100         } else if (request.assetId != null) {
101             lockResourceNames.add(request.assetId);
102         }
103     }
104
105     @Override
106     public void _exec() throws ResourceLockedException {
107         outcome = allocate(request);
108         if (outcome.status == AllocationStatus.Success) {
109             for (Resource r : updateList) {
110                 resourceDao.saveResource(r);
111             }
112         }
113     }
114
115     private AllocationOutcome allocate(AllocationRequest allocationRequest) throws ResourceLockedException {
116         if (allocationRequest instanceof MultiAssetAllocationRequest) {
117             return allocateMultiAsset((MultiAssetAllocationRequest) allocationRequest);
118         }
119         if (allocationRequest instanceof MultiResourceAllocationRequest) {
120             return allocateMultiResource((MultiResourceAllocationRequest) allocationRequest);
121         }
122         if (allocationRequest instanceof LimitAllocationRequest) {
123             return allocateLimit((LimitAllocationRequest) allocationRequest);
124         }
125         if (allocationRequest instanceof LabelAllocationRequest) {
126             return allocateLabel((LabelAllocationRequest) allocationRequest);
127         }
128         if (allocationRequest instanceof RangeAllocationRequest) {
129             return allocateRange((RangeAllocationRequest) allocationRequest);
130         }
131         return null;
132     }
133
134     private MultiAssetAllocationOutcome allocateMultiAsset(MultiAssetAllocationRequest req) {
135         // TODO Auto-generated method stub
136         return null;
137     }
138
139     private MultiResourceAllocationOutcome allocateMultiResource(MultiResourceAllocationRequest req) {
140         MultiResourceAllocationOutcome out = new MultiResourceAllocationOutcome();
141         out.request = req;
142         out.allocationOutcomeList = new ArrayList<>();
143         out.status = AllocationStatus.Success;
144
145         if (req.allocationRequestList != null) {
146             for (AllocationRequest req1 : req.allocationRequestList) {
147                 AllocationOutcome out1 = allocate(req1);
148                 out.allocationOutcomeList.add(out1);
149                 if (out1.status != AllocationStatus.Success) {
150                     out.status = AllocationStatus.Failure;
151                 }
152             }
153         }
154
155         return out;
156     }
157
158     private LimitAllocationOutcome allocateLimit(LimitAllocationRequest req) {
159         LimitAllocationOutcome out = new LimitAllocationOutcome();
160         out.request = req;
161
162         Resource r = resourceDao.getResource(req.assetId, req.resourceName);
163         if (r == null) {
164             r = new LimitResource();
165             r.resourceKey = new ResourceKey();
166             r.resourceKey.assetId = req.assetId;
167             r.resourceKey.resourceName = req.resourceName;
168             r.resourceType = ResourceType.Limit;
169         } else {
170             if (r.resourceType != ResourceType.Limit) {
171                 out.status = AllocationStatus.ResourceNotFound;
172                 return out;
173             }
174             LimitUtil.recalculate((LimitResource) r);
175         }
176
177         LimitResource l = (LimitResource) r;
178         if (LimitUtil.checkLimit(l, req)) {
179             out.status = AllocationStatus.Success;
180             if (req.allocateCount > 0) {
181                 out.allocatedCount = LimitUtil.allocateLimit(l, req);
182                 updateList.add(l);
183             }
184         } else {
185             out.status = AllocationStatus.Failure;
186         }
187
188         out.used = l.used;
189         out.limit = req.checkLimit;
190
191         return out;
192     }
193
194     private LabelAllocationOutcome allocateLabel(LabelAllocationRequest req) {
195         LabelAllocationOutcome out = new LabelAllocationOutcome();
196
197         out.request = req;
198
199         Resource r = resourceDao.getResource(req.assetId, req.resourceName);
200         if (r == null) {
201             r = new LabelResource();
202             r.resourceKey = new ResourceKey();
203             r.resourceKey.assetId = req.assetId;
204             r.resourceKey.resourceName = req.resourceName;
205             r.resourceType = ResourceType.Label;
206         } else {
207             if (r.resourceType != ResourceType.Label) {
208                 out.status = AllocationStatus.ResourceNotFound;
209                 return out;
210             }
211             LabelUtil.recalculate((LabelResource) r);
212         }
213
214         LabelResource l = (LabelResource) r;
215         if (LabelUtil.checkLabel(l, req)) {
216             out.status = AllocationStatus.Success;
217             out.currentLabel = l.label;
218             if (req.allocate) {
219                 out.allocatedLabel = LabelUtil.allocateLabel(l, req);
220                 updateList.add(l);
221             }
222         } else {
223             out.status = AllocationStatus.Failure;
224         }
225
226         return out;
227     }
228
229     private RangeAllocationOutcome allocateRange(RangeAllocationRequest req) {
230         RangeAllocationOutcome out = new RangeAllocationOutcome();
231
232         out.request = req;
233
234         Resource r = resourceDao.getResource(req.assetId, req.resourceName);
235         if (r == null) {
236             r = new RangeResource();
237             r.resourceKey = new ResourceKey();
238             r.resourceKey.assetId = req.assetId;
239             r.resourceKey.resourceName = req.resourceName;
240             r.resourceType = ResourceType.Range;
241         } else {
242             if (r.resourceType != ResourceType.Range) {
243                 out.status = AllocationStatus.ResourceNotFound;
244                 return out;
245             }
246             RangeUtil.recalculate((RangeResource) r);
247         }
248
249         RangeResource rr = (RangeResource) r;
250         SortedSet<Integer> foundNumbers = null;
251         if (!req.check) {
252             out.status = AllocationStatus.Success;
253             foundNumbers = req.requestedNumbers;
254         } else {
255             if (req.requestedNumbers != null && req.requestedNumbers.size() > 0) {
256                 foundNumbers = req.requestedNumbers;
257                 out.status = AllocationStatus.Success;
258                 for (int n : foundNumbers) {
259                     if (!RangeUtil.checkRange(rr, req, n)) {
260                         out.status = AllocationStatus.Failure;
261                         break;
262                     }
263                 }
264             } else {
265                 foundNumbers = new TreeSet<>();
266                 int foundCount = 0;
267
268                 // First try to reuse the numbers already taken by the same resource union
269                 SortedSet<Integer> uu = RangeUtil.getUsed(rr, req.resourceUnionId);
270                 if (uu != null && !uu.isEmpty() && req.replace && !req.forceNewNumbers) {
271                     if (uu.size() >= req.requestedCount) {
272                         // Just take the first req.requestedCount numbers from uu
273                         Iterator<Integer> i = uu.iterator();
274                         while (foundCount < req.requestedCount) {
275                             foundNumbers.add(i.next());
276                             foundCount++;
277                         }
278                     } else {
279                         // Additional numbers are requested. Try to find them starting from
280                         // the minimum we have in uu (the first element) towards the min
281                         // parameter, and then starting from the maximum in uu (the last
282                         // element) towards the max parameter.
283                         // NOTE: In case of request for sequential numbers, the parameters
284                         // alignBlockSize and alignModulus are ignored. It would be harder
285                         // to take them into account, and currently it is not needed.
286
287                         // Request may contain multiple ranges. We will find the range from the request
288                         // that contains the currently used numbers (the first one). We will only look
289                         // for additional numbers in that range.
290
291                         Range range = null;
292                         if (req.rangeList != null) {
293                             for (Range range1 : req.rangeList) {
294                                 if (uu.first() >= range1.min && uu.first() <= range1.max) {
295                                     range = range1;
296                                     break;
297                                 }
298                             }
299                         }
300
301                         if (range != null) {
302                             int uumin = uu.first() - 1;
303                             int uumax = uu.last() + 1;
304                             foundNumbers.addAll(uu);
305                             foundCount = uu.size();
306                             for (int n = uumin; foundCount < req.requestedCount && n >= range.min; n--) {
307                                 if (RangeUtil.checkRange(rr, req, n)) {
308                                     foundNumbers.add(n);
309                                     foundCount++;
310                                 } else if (req.sequential) {
311                                     break;
312                                 }
313                             }
314                             for (int n = uumax; foundCount < req.requestedCount && n <= range.max; n++) {
315                                 if (RangeUtil.checkRange(rr, req, n)) {
316                                     foundNumbers.add(n);
317                                     foundCount++;
318                                 } else if (req.sequential) {
319                                     break;
320                                 }
321                             }
322                         }
323
324                         // If we could not find enough numbers trying to reuse currently
325                         // allocated, reset foundNumbers and foundCount, continue with
326                         // the normal allocation of new numbers.
327                         if (foundCount < req.requestedCount) {
328                             foundNumbers = new TreeSet<>();
329                             foundCount = 0;
330                         }
331                     }
332                 }
333
334                 if (req.rangeList != null) {
335                     if (req.reverseOrder) {
336                         for (int i = req.rangeList.size() - 1; i >= 0; i--) {
337                             Range range = req.rangeList.get(i);
338                             for (int n = range.max; foundCount < req.requestedCount && n >= range.min; n--) {
339                                 if (RangeUtil.checkRange(rr, req, n)) {
340                                     foundNumbers.add(n);
341                                     foundCount++;
342                                 } else if (req.sequential) {
343                                     foundCount = 0;
344                                 }
345                             }
346                         }
347                     } else {
348                         for (Range range : req.rangeList) {
349                             for (int n = range.min; foundCount < req.requestedCount && n <= range.max; n++) {
350                                 if (RangeUtil.checkRange(rr, req, n)) {
351                                     foundNumbers.add(n);
352                                     foundCount++;
353                                 } else if (req.sequential) {
354                                     foundCount = 0;
355                                 }
356                             }
357                         }
358                     }
359                 }
360
361                 out.status = foundCount == req.requestedCount ? AllocationStatus.Success : AllocationStatus.Failure;
362             }
363         }
364
365         if (out.status == AllocationStatus.Success) {
366             out.allocated = foundNumbers;
367             if (req.allocate) {
368                 RangeUtil.allocateRange(rr, out.allocated, req);
369                 updateList.add(rr);
370             }
371         } else {
372             out.allocated = new TreeSet<>();
373         }
374
375         out.used = rr.used;
376
377         return out;
378     }
379
380     public AllocationOutcome getAllocationOutcome() {
381         return outcome;
382     }
383 }