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){