6d9b9604faf35b9a1297b7620bfebb7ceb5b99a7
[dcaegen2/services.git] /
1 /*******************************************************************************
2  *  ============LICENSE_START=======================================================
3  *  slice-analysis-ms
4  *  ================================================================================
5  *   Copyright (C) 2022 Huawei Canada Limited.
6  *  ==============================================================================
7  *     Licensed under the Apache License, Version 2.0 (the "License");
8  *     you may not use this file except in compliance with the License.
9  *     You may obtain a copy of the License at
10  *
11  *          http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *     Unless required by applicable law or agreed to in writing, software
14  *     distributed under the License is distributed on an "AS IS" BASIS,
15  *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *     See the License for the specific language governing permissions and
17  *     limitations under the License.
18  *     ============LICENSE_END=========================================================
19  *
20  *******************************************************************************/
21 package org.onap.slice.analysis.ms.service.ccvpn;
22
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.springframework.stereotype.Component;
26
27 import java.util.ArrayDeque;
28 import java.util.Map;
29 import java.util.Queue;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 import java.util.stream.Collectors;
35
36 /**
37  * This class represents the data structure for storing the CCVPN pm data;
38  */
39 @Component
40 public class CCVPNPmDatastore {
41
42     private static Logger log = LoggerFactory.getLogger(CCVPNPmDatastore.class);
43     private static final Pattern pattern = Pattern.compile("([0-9.]+)\\s*(kb|Kb|mb|Mb|Gb|gb)*");
44     private static final int WINDOW_SIZE = 5;
45     private final ConcurrentMap<String, ServiceState> svcStatus = new ConcurrentHashMap<>();
46     // Provisioned bandwidth of each endpoint
47     private final ConcurrentMap<String, Integer> endpointToProvBw = new ConcurrentHashMap<>();
48     // Max bandwidth (upper-bound) of each endpoint
49     private final ConcurrentMap<String, Integer> upperBoundBw = new ConcurrentHashMap<>();
50     // Current bandwidth usage data list from customers
51     private final ConcurrentMap<Endpointkey, EvictingQueue<Integer>> endpointToUsedBw = new ConcurrentHashMap<>();
52
53     /**
54      * Given a cllId, return a map between Endpointkey and their corresponding UsedBw Queue.
55      * All Endpoints belongs to this same service
56      * @param cllId target cll instance id
57      * @return a filtered map contains used bandwidth data of endpointkeys whose cllId equals to the given one.
58      */
59     public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwOfSvc(String cllId){
60         return endpointToUsedBw.entrySet().stream()
61                 .filter(map -> map.getKey().getCllId() == cllId)
62                 .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
63     }
64
65     /**
66      * Return the complete used bandwidth map.
67      * @return a complete endpoint to bandwidth data map
68      */
69     public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwMap(){
70         return endpointToUsedBw;
71     }
72
73     /**
74      * Return provisioned bandwidth of cll service. If provisioned bandwidth is null or missing, return 0;
75      * @param cllId target cll instance id
76      * @return Integer bandwidth value
77      */
78     public Integer getProvBwOfSvc(String cllId){
79         return endpointToProvBw.getOrDefault(cllId, 0);
80     }
81
82     /**
83      * Get Service status of this cll service
84      * @param cllId target cll instance id
85      * @return ServiceState of this cll
86      */
87     public ServiceState getStatusOfSvc(String cllId){
88         return svcStatus.getOrDefault(cllId, ServiceState.UNKNOWN);
89     }
90
91     public Integer getUpperBoundBwOfSvc(String cllId){
92         return upperBoundBw.getOrDefault(cllId, Integer.MAX_VALUE);
93     }
94
95     /**
96      * return the complete map of cll service status
97      * @return complete map of serviceStatusMap
98      */
99     public ConcurrentMap<String, ServiceState> getSvcStatusMap(){
100         return svcStatus;
101     }
102
103     /**
104      * Override the service status to provided state
105      * @param cllId target cll instance id
106      * @param state new state
107      */
108     public void updateSvcState(String cllId, ServiceState state){
109         svcStatus.put(cllId, state);
110     }
111
112     /**
113      * Update provisioned bandwidth value to given bandwidth string
114      * @param cllId target cll instance id
115      * @param bw new bandwidth
116      */
117     public void updateProvBw(String cllId, String bw){
118         double bwvvaldb = Double.parseDouble(bw);
119         int bwvval = (int) bwvvaldb;
120         updateProvBw(cllId, bwvval, false);
121     }
122
123     /**
124      * Update upper bound bandwidth value to given bandwidth
125      * @param cllId target cll instance id
126      * @param bw new bandwidth
127      */
128     public void updateUpperBoundBw(String cllId, int bw){
129         upperBoundBw.put(cllId, bw);
130     }
131
132     /**
133      * Update provisioned bandwidth to given bandwidth value;
134      * if @param{override} is false, only write the bandwidth if it is absent.
135      * Otherwise override the old value no matter if it exists or not
136      * Also, when @param{override} is true, compare the provided value with the old value, if equals, return false;
137      * otherwise, return true;
138      * @param cllId target cll instance id
139      * @param bw new bandwidth int value in Mbps
140      * @param override override old value or not
141      * @return whether bandwidth value is changed or not.
142      */
143     public boolean updateProvBw(String cllId, int bw, boolean override){
144         if (!override && !endpointToProvBw.containsKey(cllId)){
145             endpointToProvBw.put(cllId, bw);
146             return true;
147         } else {
148             if (endpointToProvBw.get(cllId) == bw){
149                 return false;
150             } else {
151                 endpointToProvBw.replace(cllId, bw);
152                 return true;
153             }
154         }
155     }
156
157     /**
158      * Append the latest bandwidth data to associated endpoint
159      * @param cllId target cll instance id
160      * @param uniId target uni id
161      * @param bw latest bandwidth usage data
162      */
163     public void addUsedBwToEndpoint(String cllId, String uniId, String bw){
164         Endpointkey enk = new Endpointkey(cllId, uniId);
165         Matcher matcher = pattern.matcher(bw.trim());
166         //Default input bw unit is kbps;
167         String unit = null;
168         // Bw in Mbps;
169         int result = 0;
170         if (matcher.find()) {
171             unit = matcher.group(2);
172             if (unit == null || unit.isEmpty() || unit.toLowerCase().equals("kb")) {
173                 double val = Double.parseDouble(matcher.group(1));
174                 result = (int) Math.ceil((double) val / (int) 1000 ) ;
175             } else if (unit.toLowerCase().equals("mb")){
176                 double val = Double.parseDouble(matcher.group(1));
177                 result = (int) val ;
178             } else if (unit.toLowerCase().equals("gb")){
179                 double val = Double.parseDouble(matcher.group(1));
180                 result = (int) val * (int) 1000;
181             }
182         } else {
183             log.warn("Illigal bw string: " + bw);
184         }
185
186         endpointToUsedBw.computeIfAbsent(enk, k -> new EvictingQueue<Integer>(WINDOW_SIZE)).offer(result);
187     }
188
189     /**
190      * Copy the used bandwidth queue of specified cllId:uniId to an array and return;
191      * @param cllId target cll id
192      * @param uniId target uni id
193      * @return Object[] contains all the used bandwidth data
194      */
195     public Object[] readToArray(String cllId, String uniId){
196         return endpointToUsedBw.get(new Endpointkey(cllId, uniId)).tryReadToArray();
197     }
198
199     /**
200      * Inner data structure is logically similar to circular buffer, thread-safe through blocking
201      * @param <E> Generic type of data
202      */
203     public class EvictingQueue<E> {
204         private final Queue<E> delegate;
205         final int maxSize;
206
207         /**
208          * Constructor accept a maxsize param
209          * @param maxSize max size
210          */
211         EvictingQueue(int maxSize){
212             if (maxSize < 0){
213                 throw new IllegalArgumentException("Invalid maxsize for initializing EvictingQueue");
214             }
215             this.delegate = new ArrayDeque<>(maxSize);
216             this.maxSize = maxSize;
217         }
218
219         /**
220          * Adding new data to this queue
221          * @param e new data
222          * @return true
223          */
224         public synchronized boolean offer(E e){
225             return add(e);
226         }
227
228         /**
229          * Try copy data to an array and return, only if data has filled up the whole queue
230          * Otherwise, return null
231          * @return the data array
232          */
233         public synchronized Object[] tryReadToArray(){
234             if (remainingCapacity() > 0){
235                 return null;
236             }
237             return toArray();
238         }
239
240         /**
241          * Return the size of this queue, and number of data added. It is no larger than the max capacity.
242          * @return int value of output
243          */
244         public int size(){
245             return delegate.size();
246         }
247
248         /**
249          * return the remaining capacity of this queue
250          * @return int value of output
251          */
252         public int remainingCapacity(){
253             return maxSize - size();
254         }
255
256         private Object[] toArray(){
257             return delegate.toArray();
258         }
259
260         private boolean add(E e){
261             if(null == e){
262                 throw new IllegalArgumentException("Invalid new item in add method");
263             }
264             if (maxSize == 0){
265                 return true;
266             }
267             if (size() == maxSize){
268                 delegate.remove();
269             }
270             delegate.add(e);
271             return true;
272         }
273     }
274 }