summaryrefslogtreecommitdiffstats
path: root/components/slice-analysis-ms/src/main/java/org/onap/slice/analysis/ms/service/ccvpn/CCVPNPmDatastore.java
blob: 6d9b9604faf35b9a1297b7620bfebb7ceb5b99a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/*******************************************************************************
 *  ============LICENSE_START=======================================================
 *  slice-analysis-ms
 *  ================================================================================
 *   Copyright (C) 2022 Huawei Canada Limited.
 *  ==============================================================================
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 *     ============LICENSE_END=========================================================
 *
 *******************************************************************************/
package org.onap.slice.analysis.ms.service.ccvpn;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayDeque;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * This class represents the data structure for storing the CCVPN pm data;
 */
@Component
public class CCVPNPmDatastore {

    private static Logger log = LoggerFactory.getLogger(CCVPNPmDatastore.class);
    private static final Pattern pattern = Pattern.compile("([0-9.]+)\\s*(kb|Kb|mb|Mb|Gb|gb)*");
    private static final int WINDOW_SIZE = 5;
    private final ConcurrentMap<String, ServiceState> svcStatus = new ConcurrentHashMap<>();
    // Provisioned bandwidth of each endpoint
    private final ConcurrentMap<String, Integer> endpointToProvBw = new ConcurrentHashMap<>();
    // Max bandwidth (upper-bound) of each endpoint
    private final ConcurrentMap<String, Integer> upperBoundBw = new ConcurrentHashMap<>();
    // Current bandwidth usage data list from customers
    private final ConcurrentMap<Endpointkey, EvictingQueue<Integer>> endpointToUsedBw = new ConcurrentHashMap<>();

    /**
     * Given a cllId, return a map between Endpointkey and their corresponding UsedBw Queue.
     * All Endpoints belongs to this same service
     * @param cllId target cll instance id
     * @return a filtered map contains used bandwidth data of endpointkeys whose cllId equals to the given one.
     */
    public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwOfSvc(String cllId){
        return endpointToUsedBw.entrySet().stream()
                .filter(map -> map.getKey().getCllId() == cllId)
                .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
    }

    /**
     * Return the complete used bandwidth map.
     * @return a complete endpoint to bandwidth data map
     */
    public Map<Endpointkey, EvictingQueue<Integer>> getUsedBwMap(){
        return endpointToUsedBw;
    }

    /**
     * Return provisioned bandwidth of cll service. If provisioned bandwidth is null or missing, return 0;
     * @param cllId target cll instance id
     * @return Integer bandwidth value
     */
    public Integer getProvBwOfSvc(String cllId){
        return endpointToProvBw.getOrDefault(cllId, 0);
    }

    /**
     * Get Service status of this cll service
     * @param cllId target cll instance id
     * @return ServiceState of this cll
     */
    public ServiceState getStatusOfSvc(String cllId){
        return svcStatus.getOrDefault(cllId, ServiceState.UNKNOWN);
    }

    public Integer getUpperBoundBwOfSvc(String cllId){
        return upperBoundBw.getOrDefault(cllId, Integer.MAX_VALUE);
    }

    /**
     * return the complete map of cll service status
     * @return complete map of serviceStatusMap
     */
    public ConcurrentMap<String, ServiceState> getSvcStatusMap(){
        return svcStatus;
    }

    /**
     * Override the service status to provided state
     * @param cllId target cll instance id
     * @param state new state
     */
    public void updateSvcState(String cllId, ServiceState state){
        svcStatus.put(cllId, state);
    }

    /**
     * Update provisioned bandwidth value to given bandwidth string
     * @param cllId target cll instance id
     * @param bw new bandwidth
     */
    public void updateProvBw(String cllId, String bw){
        double bwvvaldb = Double.parseDouble(bw);
        int bwvval = (int) bwvvaldb;
        updateProvBw(cllId, bwvval, false);
    }

    /**
     * Update upper bound bandwidth value to given bandwidth
     * @param cllId target cll instance id
     * @param bw new bandwidth
     */
    public void updateUpperBoundBw(String cllId, int bw){
        upperBoundBw.put(cllId, bw);
    }

    /**
     * Update provisioned bandwidth to given bandwidth value;
     * if @param{override} is false, only write the bandwidth if it is absent.
     * Otherwise override the old value no matter if it exists or not
     * Also, when @param{override} is true, compare the provided value with the old value, if equals, return false;
     * otherwise, return true;
     * @param cllId target cll instance id
     * @param bw new bandwidth int value in Mbps
     * @param override override old value or not
     * @return whether bandwidth value is changed or not.
     */
    public boolean updateProvBw(String cllId, int bw, boolean override){
        if (!override && !endpointToProvBw.containsKey(cllId)){
            endpointToProvBw.put(cllId, bw);
            return true;
        } else {
            if (endpointToProvBw.get(cllId) == bw){
                return false;
            } else {
                endpointToProvBw.replace(cllId, bw);
                return true;
            }
        }
    }

    /**
     * Append the latest bandwidth data to associated endpoint
     * @param cllId target cll instance id
     * @param uniId target uni id
     * @param bw latest bandwidth usage data
     */
    public void addUsedBwToEndpoint(String cllId, String uniId, String bw){
        Endpointkey enk = new Endpointkey(cllId, uniId);
        Matcher matcher = pattern.matcher(bw.trim());
        //Default input bw unit is kbps;
        String unit = null;
        // Bw in Mbps;
        int result = 0;
        if (matcher.find()) {
            unit = matcher.group(2);
            if (unit == null || unit.isEmpty() || unit.toLowerCase().equals("kb")) {
                double val = Double.parseDouble(matcher.group(1));
                result = (int) Math.ceil((double) val / (int) 1000 ) ;
            } else if (unit.toLowerCase().equals("mb")){
                double val = Double.parseDouble(matcher.group(1));
                result = (int) val ;
            } else if (unit.toLowerCase().equals("gb")){
                double val = Double.parseDouble(matcher.group(1));
                result = (int) val * (int) 1000;
            }
        } else {
            log.warn("Illigal bw string: " + bw);
        }

        endpointToUsedBw.computeIfAbsent(enk, k -> new EvictingQueue<Integer>(WINDOW_SIZE)).offer(result);
    }

    /**
     * Copy the used bandwidth queue of specified cllId:uniId to an array and return;
     * @param cllId target cll id
     * @param uniId target uni id
     * @return Object[] contains all the used bandwidth data
     */
    public Object[] readToArray(String cllId, String uniId){
        return endpointToUsedBw.get(new Endpointkey(cllId, uniId)).tryReadToArray();
    }

    /**
     * Inner data structure is logically similar to circular buffer, thread-safe through blocking
     * @param <E> Generic type of data
     */
    public class EvictingQueue<E> {
        private final Queue<E> delegate;
        final int maxSize;

        /**
         * Constructor accept a maxsize param
         * @param maxSize max size
         */
        EvictingQueue(int maxSize){
            if (maxSize < 0){
                throw new IllegalArgumentException("Invalid maxsize for initializing EvictingQueue");
            }
            this.delegate = new ArrayDeque<>(maxSize);
            this.maxSize = maxSize;
        }

        /**
         * Adding new data to this queue
         * @param e new data
         * @return true
         */
        public synchronized boolean offer(E e){
            return add(e);
        }

        /**
         * Try copy data to an array and return, only if data has filled up the whole queue
         * Otherwise, return null
         * @return the data array
         */
        public synchronized Object[] tryReadToArray(){
            if (remainingCapacity() > 0){
                return null;
            }
            return toArray();
        }

        /**
         * Return the size of this queue, and number of data added. It is no larger than the max capacity.
         * @return int value of output
         */
        public int size(){
            return delegate.size();
        }

        /**
         * return the remaining capacity of this queue
         * @return int value of output
         */
        public int remainingCapacity(){
            return maxSize - size();
        }

        private Object[] toArray(){
            return delegate.toArray();
        }

        private boolean add(E e){
            if(null == e){
                throw new IllegalArgumentException("Invalid new item in add method");
            }
            if (maxSize == 0){
                return true;
            }
            if (size() == maxSize){
                delegate.remove();
            }
            delegate.add(e);
            return true;
        }
    }
}