261794c4e403472f83895397e84641fe4e67fc5f
[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 com.google.gson.JsonObject;
25 import org.onap.slice.analysis.ms.models.Configuration;
26 import org.onap.slice.analysis.ms.service.PolicyService;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.stereotype.Component;
31
32 import javax.annotation.PostConstruct;
33 import java.util.Arrays;
34 import java.util.Map;
35 import java.util.TreeMap;
36 import java.util.stream.Collectors;
37
38 /**
39  * Threshold strategy can be configured via configuration
40  * If "sliceanalysisms.ccvpnEvalStrategy" is set to "FlexibleThresholdStrategy", then this class is triggered.
41  */
42 @Component
43 public class FlexibleThresholdStrategy implements EvaluationStrategy{
44     private static Logger log = LoggerFactory.getLogger(FixedUpperBoundStrategy.class);
45     private Configuration configuration;
46     private static final String TYPE_NAME = "FlexibleThresholdStrategy";
47     private static final String SERVICE_INSTANCE_LOCATION_ID = "service-instance-location-id";
48     private static final String BANDWIDTH_TOTAL = "bandwidth-total";
49
50     /**
51      * Percentage threshold of bandwidth increase adjustment.
52      */
53     private static double upperThreshold;
54
55     /**
56      * Percentage threshold of bandwidth decrease adjustment.
57      */
58     private static double lowerThreshold;
59
60     /**
61      * Precision of bandwidth evaluation and adjustment.
62      */
63     private static double precision; // in Mbps;
64
65     @Autowired
66     BandwidthEvaluator bandwidthEvaluator;
67
68     @Autowired
69     CCVPNPmDatastore ccvpnPmDatastore;
70
71     @Autowired
72     PolicyService policyService;
73
74     @PostConstruct
75     public void init() {
76         loadConfig();
77     }
78
79     /**
80      * Periodically ensure endpoint bw adjustment is under assurance.
81      * This method will be invoked when FlexibleThresholdStrategy is set.
82      * @param event
83      */
84     @Override
85     public void execute(Event event){
86         if (event.type() == SimpleEvent.Type.PERIODIC_CHECK && isPeriodicCheckOn()){
87             log.info("=== Processing new periodic check request: {} ===", event.time());
88             Map<Endpointkey, CCVPNPmDatastore.EvictingQueue<Integer>> usedBwMap = ccvpnPmDatastore.getUsedBwMap();
89             Map<String, Integer> candidate = new TreeMap<>();
90             for(Map.Entry<Endpointkey, CCVPNPmDatastore.EvictingQueue<Integer>> entry: usedBwMap.entrySet()) {
91                 String serviceId = entry.getKey().getCllId();
92                 Object[] usedBws = entry.getValue().tryReadToArray();
93                 // Judge whether this cll is under closed loop assurance
94                 if (!ccvpnPmDatastore.getClosedloopStatus(serviceId)) {
95                     log.info("CCVPN Evaluator Output: service {}, closed loop bw modification is off.", serviceId);
96                     continue;
97                 }
98                 if (usedBws == null) {
99                     // Not enough data for evaluating
100                     log.info("CCVPN Evaluator Output: service {}, not enough data to evaluate", serviceId);
101                     continue;
102                 }
103                 if (ccvpnPmDatastore.getProvBwOfSvc(serviceId) == 0) {
104                     // Max bandwidth not cached yet
105                     log.info("CCVPN Evaluator Output: service {}, max bandwidth not cached, wait for next round", serviceId);
106                     post(new SimpleEvent(SimpleEvent.Type.AAI_BW_REQ, serviceId));
107                     continue;
108                 }
109                 double avg = Arrays.stream(usedBws)
110                     .mapToInt(o -> (int) o)
111                     .summaryStatistics()
112                     .getAverage();
113                 int provBw = ccvpnPmDatastore.getProvBwOfSvc(serviceId);
114                 int originalBw = ccvpnPmDatastore.getOriginalBw(serviceId);
115
116                 if(needIncrease(serviceId, avg, provBw)){
117                     int newBw = (int) (Math.ceil((avg / upperThreshold) * 1.2 / precision) * precision);
118                     log.info("For cll {}, going to increase bw to {}", serviceId, newBw);
119                     candidate.put(serviceId, Math.max(candidate.getOrDefault(serviceId, 0), newBw));
120                 } else {
121                     if(needDecrease(serviceId, avg, provBw, originalBw)) {
122                         int newBw = Math.max((int) (Math.ceil(provBw * 0.5)), originalBw);
123                         log.info("For cll {}, going to decrease bw to {}", serviceId, newBw);
124                         candidate.put(serviceId, Math.max(candidate.getOrDefault(serviceId, 0), newBw));
125                     }
126                 }
127             }
128             // check svc under maintenance
129             Map<String , ServiceState> svcUnderMaintenance = getServicesUnderMaintenance();
130             for (Map.Entry<String, ServiceState> entry: svcUnderMaintenance.entrySet()){
131                 candidate.putIfAbsent(entry.getKey(), 0);
132             }
133             // fetch the provisioned bandwidth info if underMaintenance; otherwise send modification request
134             for(Map.Entry<String, Integer> entry: candidate.entrySet()) {
135                 //still doing adjustment
136                 String cllId = entry.getKey();
137                 Integer newBw = entry.getValue();
138                 if(!ccvpnPmDatastore.getClosedloopStatus(cllId)) {
139                     log.info("CCVPN Evaluator Output: service {} is not under closed loop assurance", cllId);
140                     continue;
141                 }
142                 if (isServiceUnderMaintenance(cllId)) {
143                     if (newBw == 0){
144                         log.info("CCVPN Evaluator Output: service {}," +
145                             " is in maintenance state, fetching bandwidth info from AAI", cllId);
146                     } else {
147                         log.info("CCVPN Evaluator Output: candidate {}," +
148                             " need an adjustment, but skipped due to in maintenance state", cllId);
149                     }
150                     post(new SimpleEvent(SimpleEvent.Type.AAI_BW_REQ, cllId));
151                     continue;
152                 }
153                 //not in the mid of adjustment; we are free to adjust.
154                 log.info("CCVPN Evaluator Output: candidate {}," +
155                     " need an adjustment, sending request to policy, service state changed to under maintenance", entry.getKey());
156                 ccvpnPmDatastore.updateSvcState(entry.getKey(), ServiceState.UNDER_MAINTENANCE);
157                 sendModifyRequest(entry.getKey(), newBw, RequestOwner.DCAE);
158             }
159             log.debug("=== Processing periodic check complete ===");
160         } else if (event.type() == SimpleEvent.Type.ONDEMAND_CHECK && isOnDemandCheckOn()) {
161             log.info("=== Processing upperbound adjustment request: {} ===", event.time());
162             JsonObject payload = (JsonObject) event.subject();
163             String serviceId = payload.get(SERVICE_INSTANCE_LOCATION_ID).getAsString();
164             int newBandwidth = payload.get(BANDWIDTH_TOTAL).getAsInt();
165             log.info("Update service {} bandwidth upperbound to {} ", serviceId, newBandwidth);
166             ccvpnPmDatastore.updateUpperBoundBw(serviceId, newBandwidth);
167             log.debug("=== Processing upperbound adjustment complete ===");
168         }
169     }
170
171     @Override
172     public String getName() {
173         return TYPE_NAME;
174     }
175
176     /**
177      * Post/broadcast event to the BandwidthEvaluator
178      * @param event event object
179      */
180     private void post(Event event){
181         bandwidthEvaluator.post(event);
182     }
183
184     private void loadConfig() {
185         configuration = Configuration.getInstance();
186         upperThreshold = configuration.getCcvpnEvalUpperThreshold();
187         lowerThreshold = configuration.getCcvpnEvalLowerThreshold();
188         precision = configuration.getCcvpnEvalPrecision(); // in Mbps;
189     }
190
191     private boolean isPeriodicCheckOn() {
192         configuration = Configuration.getInstance();
193         return configuration.isCcvpnEvalPeriodicCheckOn();
194     }
195
196     private boolean isOnDemandCheckOn() {
197         configuration = Configuration.getInstance();
198         return configuration.isCcvpnEvalOnDemandCheckOn();
199     }
200
201     // send modification requestion
202     private void sendModifyRequest(String cllId, Integer newBandwidth, RequestOwner owner) {
203         log.info("Sending modification request to policy. RequestOwner: {} - Service: {} change to bw: {}",
204             owner, cllId, newBandwidth);
205         policyService.sendOnsetMessageToPolicy(
206             policyService.formPolicyOnsetMessageForCCVPN(cllId, newBandwidth, owner)
207         );
208     }
209
210     private boolean needIncrease(String serviceId, double currAvgUsage, int provBw) {
211         log.info("For service {} judge whether to increase, currAvg bw {}, maxBw {}", serviceId, currAvgUsage, provBw);
212         if ( currAvgUsage > upperThreshold * provBw ) {
213             log.info("decide to increase");
214             return true;
215         }
216         return false;
217     }
218
219     private boolean needDecrease(String serviceId, double currAvgUsage, int provBw, int originalBw) {
220         log.info("For service {} judge whether to decrease, original bw {}, currAvg bw {}, prov {}", serviceId, originalBw, currAvgUsage, provBw);
221         if( currAvgUsage < lowerThreshold * provBw) {
222             log.info("decide to decrease");
223             return true;
224         }
225         return false;
226     }
227
228     // check is service under maintenance
229     private boolean isServiceUnderMaintenance(String serivceId) {
230         return ccvpnPmDatastore.getStatusOfSvc(serivceId) == ServiceState.UNDER_MAINTENANCE;
231     }
232
233     // get a collection of service under maintenance
234     private Map<String, ServiceState> getServicesUnderMaintenance(){
235         return ccvpnPmDatastore.getSvcStatusMap().entrySet()
236             .stream()
237             .filter(e -> e.getValue() == ServiceState.UNDER_MAINTENANCE)
238             .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
239     }
240 }