1 /*******************************************************************************
2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 *******************************************************************************/
22 package org.onap.slice.analysis.ms.service.ccvpn;
24 import java.util.HashSet;
25 import java.util.Iterator;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.springframework.stereotype.Component;
33 import java.util.ArrayDeque;
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;
43 * This class represents the data structure for storing the CCVPN pm data;
46 public class CCVPNPmDatastore {
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;
52 private final ConcurrentMap<String, ServiceState> svcStatus = new ConcurrentHashMap<>();
53 // Provisioned bandwidth of each endpoint
55 private final ConcurrentMap<String, Integer> endpointToProvBw = new ConcurrentHashMap<>();
56 // Max bandwidth (upper-bound) of each endpoint
58 private final ConcurrentMap<String, Integer> upperBoundBw = new ConcurrentHashMap<>();
59 // Current bandwidth usage data list from customers
61 private final ConcurrentMap<Endpointkey, EvictingQueue<Integer>> endpointToUsedBw = new ConcurrentHashMap<>();
62 // Original bandwidth of each endpoint
64 private final ConcurrentMap<String, Integer> endpointToOriginalBw = new ConcurrentHashMap<>();
65 // Assurance Status of each endpoint
67 private final ConcurrentMap<String, Boolean> closedLoopBwAssuranceStatus = new ConcurrentHashMap<>();
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.
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()));
82 * Return the complete used bandwidth map.
83 * @return a complete endpoint to bandwidth data map
85 public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwMap(){
86 return endpointToUsedBw;
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
94 public Integer getProvBwOfSvc(String cllId){
95 return endpointToProvBw.getOrDefault(cllId, 0);
99 * Get Service status of this cll service
100 * @param cllId target cll instance id
101 * @return ServiceState of this cll
103 public ServiceState getStatusOfSvc(String cllId){
104 return svcStatus.getOrDefault(cllId, ServiceState.UNKNOWN);
108 * If ccvpn flexible threshold is on, then bandwidth can be assured within scope.
112 public Integer getUpperBoundBwOfSvc(String cllId){
113 return upperBoundBw.getOrDefault(cllId, Integer.MAX_VALUE);
117 * Get closed loop check status of this cll service
121 public Boolean getClosedloopStatus(String cllId){
122 return closedLoopBwAssuranceStatus.getOrDefault(cllId,true);
125 public int getOriginalBw(String cllId) {
126 return endpointToOriginalBw.getOrDefault(cllId, 0);
131 * return the complete map of cll service status
132 * @return complete map of serviceStatusMap
134 public ConcurrentMap<String, ServiceState> getSvcStatusMap(){
139 * Override the service status to provided state
140 * @param cllId target cll instance id
141 * @param state new state
143 public void updateSvcState(String cllId, ServiceState state){
144 svcStatus.put(cllId, state);
148 * Update provisioned bandwidth value to given bandwidth string
149 * @param cllId target cll instance id
150 * @param bw new bandwidth
152 public void updateProvBw(String cllId, String bw){
153 double bwvvaldb = Double.parseDouble(bw);
154 int bwvval = (int) bwvvaldb;
155 updateProvBw(cllId, bwvval, false);
159 * Update the status, whether close loop bw modification of this cll service is on.
163 public void updateClosedloopStatus(String cllId, Boolean status){
164 closedLoopBwAssuranceStatus.put(cllId, status);
168 * Update cll original bw, which will not influenced by closed loop bw assurance
172 public void updateOriginalBw(String cllId, int originalBw){
173 endpointToOriginalBw.put(cllId, originalBw);
177 * Update runtime configurations;
179 * @param closedLoopBwAssuranceStatus
182 public void updateConfigFromPolicy(String cllId, Boolean closedLoopBwAssuranceStatus, int originalBw) {
183 updateClosedloopStatus(cllId, closedLoopBwAssuranceStatus);
184 updateOriginalBw(cllId, originalBw);
188 * Update upper bound bandwidth value to given bandwidth
189 * @param cllId target cll instance id
190 * @param bw new bandwidth
192 public void updateUpperBoundBw(String cllId, int bw){
193 upperBoundBw.put(cllId, bw);
197 * Update local service related variables in case cll is deleted.
198 * @param allValidCllInstances
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);
226 * Filter out cllId to be deleted
227 * @param allValidCllInstances
228 * @param currentCllInstances
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;
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.
248 public boolean updateProvBw(String cllId, int bw, boolean override){
249 if ( endpointToProvBw.putIfAbsent(cllId, bw) == null || !override){
252 if (endpointToProvBw.get(cllId) == bw){
255 endpointToProvBw.replace(cllId, bw);
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
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;
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));
282 } else if (unit.toLowerCase().equals("gb")){
283 double val = Double.parseDouble(matcher.group(1));
284 result = (int) val * (int) 1000;
287 log.warn("Illigal bw string: " + bw);
290 endpointToUsedBw.computeIfAbsent(enk, k -> new EvictingQueue<Integer>(WINDOW_SIZE)).offer(result);
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
299 public Object[] readToArray(String cllId, String uniId){
300 return endpointToUsedBw.get(new Endpointkey(cllId, uniId)).tryReadToArray();
304 * Inner data structure is logically similar to circular buffer, thread-safe through blocking
305 * @param <E> Generic type of data
307 public class EvictingQueue<E> {
308 private final Queue<E> delegate;
312 * Constructor accept a maxsize param
313 * @param maxSize max size
315 EvictingQueue(int maxSize){
317 throw new IllegalArgumentException("Invalid maxsize for initializing EvictingQueue");
319 this.delegate = new ArrayDeque<>(maxSize);
320 this.maxSize = maxSize;
324 * Adding new data to this queue
328 public synchronized boolean offer(E e){
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
337 public synchronized Object[] tryReadToArray(){
338 if (remainingCapacity() > 0){
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
349 return delegate.size();
353 * return the remaining capacity of this queue
354 * @return int value of output
356 public int remainingCapacity(){
357 return maxSize - size();
360 private Object[] toArray(){
361 return delegate.toArray();
364 private boolean add(E e){
366 throw new IllegalArgumentException("Invalid new item in add method");
371 if (size() == maxSize){