[CCSDK-245] RA: Refactor RA to make it generic
[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.RangeAllocationOutcome;
50 import org.onap.ccsdk.sli.adaptors.rm.data.RangeAllocationRequest;
51 import org.onap.ccsdk.sli.adaptors.rm.data.RangeResource;
52 import org.onap.ccsdk.sli.adaptors.rm.data.Resource;
53 import org.onap.ccsdk.sli.adaptors.rm.data.ResourceKey;
54 import org.onap.ccsdk.sli.adaptors.rm.data.ResourceType;
55 import org.onap.ccsdk.sli.adaptors.rm.util.LabelUtil;
56 import org.onap.ccsdk.sli.adaptors.rm.util.LimitUtil;
57 import org.onap.ccsdk.sli.adaptors.rm.util.RangeUtil;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 class AllocationFunction extends SynchronizedFunction {
62
63     @SuppressWarnings("unused")
64     private static final Logger log = LoggerFactory.getLogger(AllocationFunction.class);
65
66     private ResourceDao resourceDao;
67
68     private AllocationRequest request;
69     private AllocationOutcome outcome;
70
71     private List<Resource> updateList = new ArrayList<>();
72
73     public AllocationFunction(LockHelper lockHelper, ResourceDao resourceDao, AllocationRequest request,
74             int lockTimeout) {
75         super(lockHelper, getLockNames(request), lockTimeout);
76         this.resourceDao = resourceDao;
77         this.request = request;
78     }
79
80     private static Collection<String> getLockNames(AllocationRequest request) {
81         Set<String> lockResourceNames = new HashSet<>();
82         addLockNames(lockResourceNames, request);
83         return lockResourceNames;
84     }
85
86     private static void addLockNames(Set<String> lockResourceNames, AllocationRequest request) {
87         if (request instanceof MultiAssetAllocationRequest) {
88             MultiAssetAllocationRequest req = (MultiAssetAllocationRequest) request;
89             if (req.assetIdList != null) {
90                 lockResourceNames.addAll(req.assetIdList);
91             }
92         } else if (request instanceof MultiResourceAllocationRequest) {
93             MultiResourceAllocationRequest req = (MultiResourceAllocationRequest) request;
94             if (req.allocationRequestList != null) {
95                 for (AllocationRequest request1 : req.allocationRequestList) {
96                     addLockNames(lockResourceNames, request1);
97                 }
98             }
99         } else if (request.assetId != null) {
100             lockResourceNames.add(request.assetId);
101         }
102     }
103
104     @Override
105     public void _exec() throws ResourceLockedException {
106         outcome = allocate(request);
107         if (outcome.status == AllocationStatus.Success) {
108             for (Resource r : updateList) {
109                 resourceDao.saveResource(r);
110             }
111         }
112     }
113
114     private AllocationOutcome allocate(AllocationRequest allocationRequest) throws ResourceLockedException {
115         if (allocationRequest instanceof MultiAssetAllocationRequest) {
116             return allocateMultiAsset((MultiAssetAllocationRequest) allocationRequest);
117         }
118         if (allocationRequest instanceof MultiResourceAllocationRequest) {
119             return allocateMultiResource((MultiResourceAllocationRequest) allocationRequest);
120         }
121         if (allocationRequest instanceof LimitAllocationRequest) {
122             return allocateLimit((LimitAllocationRequest) allocationRequest);
123         }
124         if (allocationRequest instanceof LabelAllocationRequest) {
125             return allocateLabel((LabelAllocationRequest) allocationRequest);
126         }
127         if (allocationRequest instanceof RangeAllocationRequest) {
128             return allocateRange((RangeAllocationRequest) allocationRequest);
129         }
130         return null;
131     }
132
133     private MultiAssetAllocationOutcome allocateMultiAsset(MultiAssetAllocationRequest req) {
134         // TODO Auto-generated method stub
135         return null;
136     }
137
138     private MultiResourceAllocationOutcome allocateMultiResource(MultiResourceAllocationRequest req) {
139         MultiResourceAllocationOutcome out = new MultiResourceAllocationOutcome();
140         out.request = req;
141         out.allocationOutcomeList = new ArrayList<>();
142         out.status = AllocationStatus.Success;
143
144         if (req.allocationRequestList != null) {
145             for (AllocationRequest req1 : req.allocationRequestList) {
146                 AllocationOutcome out1 = allocate(req1);
147                 out.allocationOutcomeList.add(out1);
148                 if (out1.status != AllocationStatus.Success) {
149                     out.status = AllocationStatus.Failure;
150                 }
151             }
152         }
153
154         return out;
155     }
156
157     private LimitAllocationOutcome allocateLimit(LimitAllocationRequest req) {
158         LimitAllocationOutcome out = new LimitAllocationOutcome();
159         out.request = req;
160
161         Resource r = resourceDao.getResource(req.assetId, req.resourceName);
162         if (r == null) {
163             r = new LimitResource();
164             r.resourceKey = new ResourceKey();
165             r.resourceKey.assetId = req.assetId;
166             r.resourceKey.resourceName = req.resourceName;
167             r.resourceType = ResourceType.Limit;
168         } else {
169             if (r.resourceType != ResourceType.Limit) {
170                 out.status = AllocationStatus.ResourceNotFound;
171                 return out;
172             }
173             LimitUtil.recalculate((LimitResource) r);
174         }
175
176         LimitResource l = (LimitResource) r;
177         if (LimitUtil.checkLimit(l, req)) {
178             out.status = AllocationStatus.Success;
179             if (req.allocateCount > 0) {
180                 out.allocatedCount = LimitUtil.allocateLimit(l, req);
181                 updateList.add(l);
182             }
183         } else {
184             out.status = AllocationStatus.Failure;
185         }
186
187         out.used = l.used;
188         out.limit = req.checkLimit;
189
190         return out;
191     }
192
193     private LabelAllocationOutcome allocateLabel(LabelAllocationRequest req) {
194         LabelAllocationOutcome out = new LabelAllocationOutcome();
195
196         out.request = req;
197
198         Resource r = resourceDao.getResource(req.assetId, req.resourceName);
199         if (r == null) {
200             r = new LabelResource();
201             r.resourceKey = new ResourceKey();
202             r.resourceKey.assetId = req.assetId;
203             r.resourceKey.resourceName = req.resourceName;
204             r.resourceType = ResourceType.Label;
205         } else {
206             if (r.resourceType != ResourceType.Label) {
207                 out.status = AllocationStatus.ResourceNotFound;
208                 return out;
209             }
210             LabelUtil.recalculate((LabelResource) r);
211         }
212
213         LabelResource l = (LabelResource) r;
214         if (LabelUtil.checkLabel(l, req)) {
215             out.status = AllocationStatus.Success;
216             out.currentLabel = l.label;
217             if (req.allocate) {
218                 out.allocatedLabel = LabelUtil.allocateLabel(l, req);
219                 updateList.add(l);
220             }
221         } else {
222             out.status = AllocationStatus.Failure;
223         }
224
225         return out;
226     }
227
228     private RangeAllocationOutcome allocateRange(RangeAllocationRequest req) {
229         RangeAllocationOutcome out = new RangeAllocationOutcome();
230
231         out.request = req;
232
233         Resource r = resourceDao.getResource(req.assetId, req.resourceName);
234         if (r == null) {
235             r = new RangeResource();
236             r.resourceKey = new ResourceKey();
237             r.resourceKey.assetId = req.assetId;
238             r.resourceKey.resourceName = req.resourceName;
239             r.resourceType = ResourceType.Range;
240         } else {
241             if (r.resourceType != ResourceType.Range) {
242                 out.status = AllocationStatus.ResourceNotFound;
243                 return out;
244             }
245             RangeUtil.recalculate((RangeResource) r);
246         }
247
248         RangeResource rr = (RangeResource) r;
249         SortedSet<Integer> foundNumbers = null;
250         if (!req.check) {
251             out.status = AllocationStatus.Success;
252             foundNumbers = req.requestedNumbers;
253         } else {
254             if (req.requestedNumbers != null && req.requestedNumbers.size() > 0) {
255                 foundNumbers = req.requestedNumbers;
256                 out.status = AllocationStatus.Success;
257                 for (int n : foundNumbers) {
258                     if (!RangeUtil.checkRange(rr, req, n)) {
259                         out.status = AllocationStatus.Failure;
260                         break;
261                     }
262                 }
263             } else {
264                 foundNumbers = new TreeSet<>();
265                 int foundCount = 0;
266
267                 // First try to reuse the numbers already taken by the same resource union
268                 SortedSet<Integer> uu = RangeUtil.getUsed(rr, req.resourceUnionId);
269                 if (uu != null && !uu.isEmpty() && req.replace) {
270                     if (uu.size() >= req.requestedCount) {
271                         // Just take the first req.requestedCount numbers from uu
272                         Iterator<Integer> i = uu.iterator();
273                         while (foundCount < req.requestedCount) {
274                             foundNumbers.add(i.next());
275                             foundCount++;
276                         }
277                     } else {
278                         // Additional numbers are requested. Try to find them starting from
279                         // the minimum we have in uu (the first element) towards the min
280                         // parameter, and then starting from the maximum in uu (the last
281                         // element) towards the max parameter.
282                         // NOTE: In case of request for sequential numbers, the parameters
283                         // alignBlockSize and alignModulus are ignored. It would be harder
284                         // to take them into account, and currently it is not needed.
285
286                         int uumin = uu.first() - 1;
287                         int uumax = uu.last() + 1;
288                         foundNumbers.addAll(uu);
289                         foundCount = uu.size();
290                         for (int n = uumin; foundCount < req.requestedCount && n >= req.checkMin; n--) {
291                             if (RangeUtil.checkRange(rr, req, n)) {
292                                 foundNumbers.add(n);
293                                 foundCount++;
294                             } else if (req.sequential) {
295                                 break;
296                             }
297                         }
298                         for (int n = uumax; foundCount < req.requestedCount && n <= req.checkMax; n++) {
299                             if (RangeUtil.checkRange(rr, req, n)) {
300                                 foundNumbers.add(n);
301                                 foundCount++;
302                             } else if (req.sequential) {
303                                 break;
304                             }
305                         }
306
307                         // If we could not find enough numbers trying to reuse currently
308                         // allocated, reset foundNumbers and foundCount, continue with
309                         // the normal allocation of new numbers.
310                         if (foundCount < req.requestedCount) {
311                             foundNumbers = new TreeSet<>();
312                             foundCount = 0;
313                         }
314                     }
315                 }
316
317                 if (req.reverseOrder) {
318                     for (int n = req.checkMax; foundCount < req.requestedCount && n >= req.checkMin; n--) {
319                         if (RangeUtil.checkRange(rr, req, n)) {
320                             foundNumbers.add(n);
321                             foundCount++;
322                         } else if (req.sequential) {
323                             foundCount = 0;
324                         }
325                     }
326                 } else {
327                     for (int n = req.checkMin; foundCount < req.requestedCount && n <= req.checkMax; n++) {
328                         if (RangeUtil.checkRange(rr, req, n)) {
329                             foundNumbers.add(n);
330                             foundCount++;
331                         } else if (req.sequential) {
332                             foundCount = 0;
333                         }
334                     }
335                 }
336
337                 out.status = foundCount == req.requestedCount ? AllocationStatus.Success : AllocationStatus.Failure;
338             }
339         }
340
341         if (out.status == AllocationStatus.Success) {
342             out.allocated = foundNumbers;
343             if (req.allocate) {
344                 RangeUtil.allocateRange(rr, out.allocated, req);
345                 updateList.add(rr);
346             }
347         } else {
348             out.allocated = new TreeSet<>();
349         }
350
351         out.used = rr.used;
352
353         return out;
354     }
355
356     public AllocationOutcome getAllocationOutcome() {
357         return outcome;
358     }
359 }