5f3ce3111b70b13bff69bfcb4f2dc1f8f0a41ff3
[dcaegen2/services.git] /
1 /*******************************************************************************
2  *  ============LICENSE_START=======================================================
3  *  slice-analysis-ms
4  *  ================================================================================
5  *   Copyright (C) 2022 Huawei Canada Limited.
6  *   Copyright (C) 2022 Huawei Technologies Co., Ltd.
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.slice.analysis.ms.service.ccvpn;
23
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.Set;
27 import lombok.Getter;
28 import lombok.Setter;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.springframework.stereotype.Component;
32
33 import java.util.ArrayDeque;
34 import java.util.Map;
35 import java.util.Queue;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ConcurrentMap;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40 import java.util.stream.Collectors;
41
42 /**
43  * This class represents the data structure for storing the CCVPN pm data;
44  */
45 @Component
46 public class CCVPNPmDatastore {
47
48     private static Logger log = LoggerFactory.getLogger(CCVPNPmDatastore.class);
49     private static final Pattern pattern = Pattern.compile("([0-9.]+)\\s*(kb|Kb|mb|Mb|Gb|gb)*");
50     private static final int WINDOW_SIZE = 5;
51     @Getter
52     private final ConcurrentMap<String, ServiceState> svcStatus = new ConcurrentHashMap<>();
53     // Provisioned bandwidth of each endpoint
54     @Getter
55     private final ConcurrentMap<String, Integer> endpointToProvBw = new ConcurrentHashMap<>();
56     // Max bandwidth (upper-bound) of each endpoint
57     @Getter
58     private final ConcurrentMap<String, Integer> upperBoundBw = new ConcurrentHashMap<>();
59     // Current bandwidth usage data list from customers
60     @Getter
61     private final ConcurrentMap<Endpointkey, EvictingQueue<Integer>> endpointToUsedBw = new ConcurrentHashMap<>();
62     // Original bandwidth of each endpoint
63     @Getter
64     private final ConcurrentMap<String, Integer> endpointToOriginalBw = new ConcurrentHashMap<>();
65     // Assurance Status of each endpoint
66     @Getter
67     private final ConcurrentMap<String, Boolean> closedLoopBwAssuranceStatus = new ConcurrentHashMap<>();
68
69     /**
70      * Given a cllId, return a map between Endpointkey and their corresponding UsedBw Queue.
71      * All Endpoints belongs to this same service
72      * @param cllId target cll instance id
73      * @return a filtered map contains used bandwidth data of endpointkeys whose cllId equals to the given one.
74      */
75     public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwOfSvc(String cllId){
76         return endpointToUsedBw.entrySet().stream()
77                 .filter(map -> map.getKey().getCllId() == cllId)
78                 .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
79     }
80
81     /**
82      * Return the complete used bandwidth map.
83      * @return a complete endpoint to bandwidth data map
84      */
85     public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwMap(){
86         return endpointToUsedBw;
87     }
88
89     /**
90      * Return provisioned bandwidth of cll service. If provisioned bandwidth is null or missing, return 0;
91      * @param cllId target cll instance id
92      * @return Integer bandwidth value
93      */
94     public Integer getProvBwOfSvc(String cllId){
95         return endpointToProvBw.getOrDefault(cllId, 0);
96     }
97
98     /**
99      * Get Service status of this cll service
100      * @param cllId target cll instance id
101      * @return ServiceState of this cll
102      */
103     public ServiceState getStatusOfSvc(String cllId){
104         return svcStatus.getOrDefault(cllId, ServiceState.UNKNOWN);
105     }
106
107     /**
108      * If ccvpn flexible threshold is on, then bandwidth can be assured within scope.
109      * @param cllId
110      * @return
111      */
112     public Integer getUpperBoundBwOfSvc(String cllId){
113         return upperBoundBw.getOrDefault(cllId, Integer.MAX_VALUE);
114     }
115
116     /**
117      * Get closed loop check status of this cll service
118      * @param cllId
119      * @return
120      */
121     public Boolean getClosedloopStatus(String cllId){
122         return closedLoopBwAssuranceStatus.getOrDefault(cllId,true);
123     }
124
125     public int getOriginalBw(String cllId) {
126         return endpointToOriginalBw.getOrDefault(cllId, 0);
127     }
128
129
130     /**
131      * return the complete map of cll service status
132      * @return complete map of serviceStatusMap
133      */
134     public ConcurrentMap<String, ServiceState> getSvcStatusMap(){
135         return svcStatus;
136     }
137
138     /**
139      * Override the service status to provided state
140      * @param cllId target cll instance id
141      * @param state new state
142      */
143     public void updateSvcState(String cllId, ServiceState state){
144         svcStatus.put(cllId, state);
145     }
146
147     /**
148      * Update provisioned bandwidth value to given bandwidth string
149      * @param cllId target cll instance id
150      * @param bw new bandwidth
151      */
152     public void updateProvBw(String cllId, String bw){
153         double bwvvaldb = Double.parseDouble(bw);
154         int bwvval = (int) bwvvaldb;
155         updateProvBw(cllId, bwvval, false);
156     }
157
158     /**
159      * Update the status, whether close loop bw modification of this cll service is on.
160      * @param cllId
161      * @param status
162      */
163     public void updateClosedloopStatus(String cllId, Boolean status){
164         closedLoopBwAssuranceStatus.put(cllId, status);
165     }
166
167     /**
168      * Update cll original bw, which will not influenced by closed loop bw assurance
169      * @param cllId
170      * @param originalBw
171      */
172     public void updateOriginalBw(String cllId, int originalBw){
173         endpointToOriginalBw.put(cllId, originalBw);
174     }
175
176     /**
177      * Update runtime configurations;
178      * @param cllId
179      * @param closedLoopBwAssuranceStatus
180      * @param originalBw
181      */
182     public void updateConfigFromPolicy(String cllId, Boolean closedLoopBwAssuranceStatus, int originalBw) {
183         updateClosedloopStatus(cllId, closedLoopBwAssuranceStatus);
184         updateOriginalBw(cllId, originalBw);
185     }
186
187     /**
188      * Update upper bound bandwidth value to given bandwidth
189      * @param cllId target cll instance id
190      * @param bw new bandwidth
191      */
192     public void updateUpperBoundBw(String cllId, int bw){
193         upperBoundBw.put(cllId, bw);
194     }
195
196     /**
197      * Update local service related variables in case cll is deleted.
198      * @param allValidCllInstances
199      */
200     public void updateCllInstances(Set<String> allValidCllInstances){
201         Set<String> invalidCllIds;
202         invalidCllIds= filterInvalidCllIds(allValidCllInstances, svcStatus.keySet());
203         svcStatus.keySet().removeAll(invalidCllIds);
204         invalidCllIds = filterInvalidCllIds(allValidCllInstances, endpointToProvBw.keySet());
205         endpointToProvBw.keySet().removeAll(invalidCllIds);
206         invalidCllIds = filterInvalidCllIds(allValidCllInstances, upperBoundBw.keySet());
207         upperBoundBw.keySet().removeAll(invalidCllIds);
208         invalidCllIds = filterInvalidCllIds(allValidCllInstances, endpointToOriginalBw.keySet());
209         endpointToOriginalBw.keySet().removeAll(invalidCllIds);
210         invalidCllIds = filterInvalidCllIds(allValidCllInstances, closedLoopBwAssuranceStatus.keySet());
211         closedLoopBwAssuranceStatus.keySet().removeAll(invalidCllIds);
212         for(String invalidCllId : invalidCllIds) {
213             log.debug("drop {} from endpointToUsedBw", invalidCllId);
214             endpointToUsedBw.entrySet().stream().dropWhile(map -> map.getKey().getCllId().equalsIgnoreCase(invalidCllId));
215             Iterator<Map.Entry<Endpointkey, EvictingQueue<Integer>>> iterator = endpointToUsedBw.entrySet().iterator();
216             while(iterator.hasNext()) {
217                 Endpointkey endpointkey = iterator.next().getKey();
218                 if(endpointkey.getCllId().equalsIgnoreCase(invalidCllId)) {
219                     endpointToUsedBw.remove(endpointkey);
220                 }
221             }
222         }
223     }
224
225     /**
226      * Filter out cllId to be deleted
227      * @param allValidCllInstances
228      * @param currentCllInstances
229      * @return
230      */
231     public Set<String> filterInvalidCllIds(Set<String> allValidCllInstances, Set<String> currentCllInstances) {
232         Set<String> invalidCllInstances = new HashSet<>(currentCllInstances);
233         invalidCllInstances.removeAll(allValidCllInstances);
234         return invalidCllInstances;
235     }
236
237     /**
238      * Update provisioned bandwidth to given bandwidth value;
239      * if @param{override} is false, only write the bandwidth if it is absent.
240      * Otherwise override the old value no matter if it exists or not
241      * Also, when @param{override} is true, compare the provided value with the old value, if equals, return false;
242      * otherwise, return true;
243      * @param cllId target cll instance id
244      * @param bw new bandwidth int value in Mbps
245      * @param override override old value or not
246      * @return whether bandwidth value is changed or not.
247      */
248     public boolean updateProvBw(String cllId, int bw, boolean override){
249         if ( endpointToProvBw.putIfAbsent(cllId, bw) == null || !override){
250             return true;
251         } else {
252             if (endpointToProvBw.get(cllId) == bw){
253                 return false;
254             } else {
255                 endpointToProvBw.replace(cllId, bw);
256                 return true;
257             }
258         }
259     }
260
261     /**
262      * Append the latest bandwidth data to associated endpoint
263      * @param cllId target cll instance id
264      * @param uniId target uni id
265      * @param bw latest bandwidth usage data
266      */
267     public void addUsedBwToEndpoint(String cllId, String uniId, String bw){
268         Endpointkey enk = new Endpointkey(cllId, uniId);
269         Matcher matcher = pattern.matcher(bw.trim());
270         //Default input bw unit is kbps;
271         String unit = null;
272         // Bw in Mbps;
273         int result = 0;
274         if (matcher.find()) {
275             unit = matcher.group(2);
276             if (unit == null || unit.isEmpty() || unit.toLowerCase().equals("kb")) {
277                 double val = Double.parseDouble(matcher.group(1));
278                 result = (int) Math.ceil((double) val / (int) 1000 ) ;
279             } else if (unit.toLowerCase().equals("mb")){
280                 double val = Double.parseDouble(matcher.group(1));
281                 result = (int) val ;
282             } else if (unit.toLowerCase().equals("gb")){
283                 double val = Double.parseDouble(matcher.group(1));
284                 result = (int) val * (int) 1000;
285             }
286         } else {
287             log.warn("Illigal bw string: " + bw);
288         }
289
290         endpointToUsedBw.computeIfAbsent(enk, k -> new EvictingQueue<Integer>(WINDOW_SIZE)).offer(result);
291     }
292
293     /**
294      * Copy the used bandwidth queue of specified cllId:uniId to an array and return;
295      * @param cllId target cll id
296      * @param uniId target uni id
297      * @return Object[] contains all the used bandwidth data
298      */
299     public Object[] readToArray(String cllId, String uniId){
300         return endpointToUsedBw.get(new Endpointkey(cllId, uniId)).tryReadToArray();
301     }
302
303     /**
304      * Inner data structure is logically similar to circular buffer, thread-safe through blocking
305      * @param <E> Generic type of data
306      */
307     public class EvictingQueue<E> {
308         private final Queue<E> delegate;
309         final int maxSize;
310
311         /**
312          * Constructor accept a maxsize param
313          * @param maxSize max size
314          */
315         EvictingQueue(int maxSize){
316             if (maxSize < 0){
317                 throw new IllegalArgumentException("Invalid maxsize for initializing EvictingQueue");
318             }
319             this.delegate = new ArrayDeque<>(maxSize);
320             this.maxSize = maxSize;
321         }
322
323         /**
324          * Adding new data to this queue
325          * @param e new data
326          * @return true
327          */
328         public synchronized boolean offer(E e){
329             return add(e);
330         }
331
332         /**
333          * Try copy data to an array and return, only if data has filled up the whole queue
334          * Otherwise, return null
335          * @return the data array
336          */
337         public synchronized Object[] tryReadToArray(){
338             if (remainingCapacity() > 0){
339                 return null;
340             }
341             return toArray();
342         }
343
344         /**
345          * Return the size of this queue, and number of data added. It is no larger than the max capacity.
346          * @return int value of output
347          */
348         public int size(){
349             return delegate.size();
350         }
351
352         /**
353          * return the remaining capacity of this queue
354          * @return int value of output
355          */
356         public int remainingCapacity(){
357             return maxSize - size();
358         }
359
360         private Object[] toArray(){
361             return delegate.toArray();
362         }
363
364         private boolean add(E e){
365             if(null == e){
366                 throw new IllegalArgumentException("Invalid new item in add method");
367             }
368             if (maxSize == 0){
369                 return true;
370             }
371             if (size() == maxSize){
372                 delegate.remove();
373             }
374             delegate.add(e);
375             return true;
376         }
377     }
378 }