874e327112883756e0b4857dfa98894b9ff18b00
[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 com.google.gson.JsonObject;
24 import org.onap.slice.analysis.ms.models.Configuration;
25 import org.onap.slice.analysis.ms.service.PolicyService;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.springframework.beans.factory.annotation.Autowired;
29 import org.springframework.stereotype.Component;
30
31 import javax.annotation.PostConstruct;
32 import java.util.Arrays;
33 import java.util.Map;
34 import java.util.TreeMap;
35 import java.util.stream.Collectors;
36
37 @Component
38 public class FixedUpperBoundStrategy implements EvaluationStrategy{
39     private static Logger log = LoggerFactory.getLogger(FixedUpperBoundStrategy.class);
40     private Configuration configuration;
41
42     private static final String TYPE_NAME = "FixedUpperBoundStrategy";
43     private static final String SERVICE_INSTANCE_LOCATION_ID = "service-instance-location-id";
44     private static final String BANDWIDTH_TOTAL = "bandwidth-total";
45
46     /**
47      * Percentage threshold of bandwidth adjustment.
48      */
49     private static double threshold;
50
51     /**
52      * Precision of bandwidth evaluation and adjustment.
53      */
54     private static double precision; // in Mbps;
55
56     @Autowired
57     BandwidthEvaluator bandwidthEvaluator;
58
59     @Autowired
60     CCVPNPmDatastore ccvpnPmDatastore;
61
62     @Autowired
63     PolicyService policyService;
64
65     @PostConstruct
66     public void init() {
67         loadConfig();
68     }
69
70     @Override
71     public void execute(Event event){
72         if (event.type() == SimpleEvent.Type.PERIODIC_CHECK && isPeriodicCheckOn()){
73             log.debug("=== Processing new periodic check request: {} ===", event.time());
74             Map<Endpointkey, CCVPNPmDatastore.EvictingQueue<Integer>> usedBwMap = ccvpnPmDatastore.getUsedBwMap();
75             Map<String, Integer> candidate = new TreeMap<>();
76             for(Map.Entry<Endpointkey, CCVPNPmDatastore.EvictingQueue<Integer>> entry: usedBwMap.entrySet()) {
77                 String serviceId = entry.getKey().getCllId();
78                 Object[] usedBws = entry.getValue().tryReadToArray();
79
80                 if (usedBws == null) {
81                     // No enough data for evaluating
82                     log.debug("CCVPN Evaluator Output: service {}, not enough data to evaluate", serviceId);
83                     continue;
84                 }
85                 if (ccvpnPmDatastore.getProvBwOfSvc(serviceId) == 0) {
86                     // Max bandwidth not cached yet
87                     log.debug("CCVPN Evaluator Output: service {}, max bandwidth not cached, wait for next round", serviceId);
88                     post(new SimpleEvent(SimpleEvent.Type.AAI_BW_REQ, serviceId));
89                     continue;
90                 }
91                 double avg = Arrays.stream(usedBws)
92                         .mapToInt(o -> (int) o)
93                         .summaryStatistics()
94                         .getAverage();
95                 int provBw = ccvpnPmDatastore.getProvBwOfSvc(serviceId);
96                 int upperBw = ccvpnPmDatastore.getUpperBoundBwOfSvc(serviceId);
97                 if (needAdjust(serviceId, avg, provBw, upperBw)) {
98                     int newBw = needAdjustTo(serviceId, avg, provBw, upperBw);
99                     if(Math.abs(newBw - provBw) >= precision){
100                         log.info("CCVPN Evaluator Output: service {}, need adjustment, putting into candidate list", serviceId);
101                         candidate.put(serviceId, newBw);
102                     }
103                 }
104             }
105             // check svc under maintenance
106             Map<String , ServiceState> svcUnderMaintenance = getServicesUnderMaintenance();
107             for (Map.Entry<String, ServiceState> entry: svcUnderMaintenance.entrySet()){
108                 candidate.putIfAbsent(entry.getKey(), 0);
109             }
110             // fetch the provisioned bandwidth info if underMaintenance; otherwise send modification request
111             for(Map.Entry<String, Integer> entry: candidate.entrySet()) {
112                 //still doing adjustment
113                 if (isServiceUnderMaintenance(entry.getKey())) {
114                     if (entry.getValue() == 0){
115                         log.debug("CCVPN Evaluator Output: service {}," +
116                                 " is in maintenance state, fetching bandwidth info from AAI", entry.getKey());
117                     } else {
118                         log.debug("CCVPN Evaluator Output: candidate {}," +
119                                 " need an adjustment, but skipped due to in maintenance state", entry.getKey());
120                     }
121                     post(new SimpleEvent(SimpleEvent.Type.AAI_BW_REQ, entry.getKey()));
122                     continue;
123                 }
124                 //not in the mid of adjustment; we are free to adjust.
125                 log.info("CCVPN Evaluator Output: candidate {}," +
126                         " need an adjustment, sending request to policy", entry.getKey());
127                 ccvpnPmDatastore.updateSvcState(entry.getKey(), ServiceState.UNDER_MAINTENANCE);
128                 sendModifyRequest(entry.getKey(), entry.getValue(), RequestOwner.DCAE);
129             }
130             log.debug("=== Processing periodic check complete ===");
131         }
132         if (event.type() == SimpleEvent.Type.ONDEMAND_CHECK && isOnDemandCheckOn()) {
133             log.debug("=== Processing upperbound adjustment request: {} ===", event.time());
134             JsonObject payload = (JsonObject) event.subject();
135             String serviceId = payload.get(SERVICE_INSTANCE_LOCATION_ID).getAsString();
136             int newBandwidth = payload.get(BANDWIDTH_TOTAL).getAsInt();
137             log.info("Update service {} bandwidth upperbound to {} ", serviceId, newBandwidth);
138             ccvpnPmDatastore.updateUpperBoundBw(serviceId, newBandwidth);
139             log.debug("=== Processing upperbound adjustment complete ===");
140         }
141     }
142
143     @Override
144     public String getName() {
145         return TYPE_NAME;
146     }
147
148     /**
149      * Post/broadcast event to the BandwidthEvaluator
150      * @param event event object
151      */
152     private void post(Event event){
153         bandwidthEvaluator.post(event);
154     }
155
156     private void loadConfig() {
157         configuration = Configuration.getInstance();
158         threshold = configuration.getCcvpnEvalThreshold();
159         precision = configuration.getCcvpnEvalPrecision(); // in Mbps;
160     }
161
162     private boolean isPeriodicCheckOn() {
163         configuration = Configuration.getInstance();
164         return configuration.isCcvpnEvalPeriodicCheckOn();
165     }
166
167     private boolean isOnDemandCheckOn() {
168         configuration = Configuration.getInstance();
169         return configuration.isCcvpnEvalOnDemandCheckOn();
170     }
171
172     // send modification requestion
173     private void sendModifyRequest(String cllId, Integer newBandwidth, RequestOwner owner) {
174         log.info("Sending modification request to policy. RequestOwner: {} - Service: {} change to bw: {}",
175                 owner, cllId, newBandwidth);
176         policyService.sendOnsetMessageToPolicy(
177                 policyService.formPolicyOnsetMessageForCCVPN(cllId, newBandwidth, owner)
178         );
179     }
180     // check if an adjustment is necessary
181     private boolean needAdjust(String serivceId, double used, int provBandwidth, int upper){
182         log.debug("CCVPN Service Usage Analysis: usage: {}, threshold: {}, currentProvisioned {}, upperbound {}",
183                 used, threshold, provBandwidth, upper);
184         return provBandwidth > upper || used > threshold * provBandwidth;
185     }
186
187     // calculate new bandwidth to accomodate customer
188     private int needAdjustTo(String serivceId, double used, int cur, int upper){
189         if (cur >= upper){
190             return upper;
191         }
192         int expected = (int) (Math.ceil((used / threshold) * 1.2 / precision) * precision);
193         return Math.min(expected, upper);
194     }
195     // check is service under maint
196     private boolean isServiceUnderMaintenance(String serivceId) {
197         return ccvpnPmDatastore.getStatusOfSvc(serivceId) == ServiceState.UNDER_MAINTENANCE;
198     }
199     // get a collection of service under maint
200     private Map<String, ServiceState> getServicesUnderMaintenance(){
201         return ccvpnPmDatastore.getSvcStatusMap().entrySet()
202                 .stream()
203                 .filter(e -> e.getValue() == ServiceState.UNDER_MAINTENANCE)
204                 .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
205     }
206 }