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 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;
 
  32 import javax.annotation.PostConstruct;
 
  33 import java.util.Arrays;
 
  35 import java.util.TreeMap;
 
  36 import java.util.stream.Collectors;
 
  39  * Threshold strategy can be configured via configuration
 
  40  * If "sliceanalysisms.ccvpnEvalStrategy" is set to "FixedUpperBoundStrategy", then this class is triggered.
 
  43 public class FixedUpperBoundStrategy implements EvaluationStrategy{
 
  44     private static Logger log = LoggerFactory.getLogger(FixedUpperBoundStrategy.class);
 
  45     private Configuration configuration;
 
  47     private static final String TYPE_NAME = "FixedUpperBoundStrategy";
 
  48     private static final String SERVICE_INSTANCE_LOCATION_ID = "service-instance-location-id";
 
  49     private static final String BANDWIDTH_TOTAL = "bandwidth-total";
 
  52      * Percentage threshold of bandwidth adjustment.
 
  54     private static double upperThreshold;
 
  57      * Precision of bandwidth evaluation and adjustment.
 
  59     private static double precision; // in Mbps;
 
  62     BandwidthEvaluator bandwidthEvaluator;
 
  65     CCVPNPmDatastore ccvpnPmDatastore;
 
  68     PolicyService policyService;
 
  76      * Periodically ensure endpoint bw adjustment is under assurance.
 
  77      * This method will be invoked when FixedUpperBoundStrategy is set.
 
  81     public void execute(Event event){
 
  82         if (event.type() == SimpleEvent.Type.PERIODIC_CHECK && isPeriodicCheckOn()){
 
  83             log.debug("=== Processing new periodic check request: {} ===", event.time());
 
  84             Map<Endpointkey, CCVPNPmDatastore.EvictingQueue<Integer>> usedBwMap = ccvpnPmDatastore.getUsedBwMap();
 
  85             Map<String, Integer> candidate = new TreeMap<>();
 
  86             for(Map.Entry<Endpointkey, CCVPNPmDatastore.EvictingQueue<Integer>> entry: usedBwMap.entrySet()) {
 
  87                 String serviceId = entry.getKey().getCllId();
 
  88                 Object[] usedBws = entry.getValue().tryReadToArray();
 
  90                 if (!ccvpnPmDatastore.getClosedloopStatus(serviceId)) {
 
  91                     log.info("CCVPN Evaluator Output: service {}, closed loop bw modification is off.", serviceId);
 
  94                 if (usedBws == null) {
 
  95                     // No enough data for evaluating
 
  96                     log.info("CCVPN Evaluator Output: service {}, not enough data to evaluate", serviceId);
 
  99                 if (ccvpnPmDatastore.getProvBwOfSvc(serviceId) == 0) {
 
 100                     // Max bandwidth not cached yet
 
 101                     log.info("CCVPN Evaluator Output: service {}, max bandwidth not cached, wait for next round", serviceId);
 
 102                     post(new SimpleEvent(SimpleEvent.Type.AAI_BW_REQ, serviceId));
 
 105                 double avg = Arrays.stream(usedBws)
 
 106                         .mapToInt(o -> (int) o)
 
 109                 int provBw = ccvpnPmDatastore.getProvBwOfSvc(serviceId);
 
 110                 int upperBw = ccvpnPmDatastore.getUpperBoundBwOfSvc(serviceId);
 
 111                 if (needAdjust(serviceId, avg, provBw, upperBw)) {
 
 112                     int newBw = needAdjustTo(serviceId, avg, provBw, upperBw);
 
 113                     if(Math.abs(newBw - provBw) >= precision){
 
 114                         log.info("CCVPN Evaluator Output: service {}, need adjustment, putting into candidate list", serviceId);
 
 115                         candidate.put(serviceId, newBw);
 
 119             // check svc under maintenance
 
 120             Map<String , ServiceState> svcUnderMaintenance = getServicesUnderMaintenance();
 
 121             for (Map.Entry<String, ServiceState> entry: svcUnderMaintenance.entrySet()){
 
 122                 candidate.putIfAbsent(entry.getKey(), 0);
 
 124             // fetch the provisioned bandwidth info if underMaintenance; otherwise send modification request
 
 125             for(Map.Entry<String, Integer> entry: candidate.entrySet()) {
 
 126                 //still doing adjustment
 
 127                 String cllId = entry.getKey();
 
 128                 Integer newBw = entry.getValue();
 
 129                 if(!ccvpnPmDatastore.getClosedloopStatus(cllId)) {
 
 130                     log.info("CCVPN Evaluator Output: service {} is not under closed loop assurance", cllId);
 
 133                 if (isServiceUnderMaintenance(cllId)) {
 
 135                         log.info("CCVPN Evaluator Output: service {}," +
 
 136                             " is in maintenance state, fetching bandwidth info from AAI", cllId);
 
 138                         log.info("CCVPN Evaluator Output: candidate {}," +
 
 139                             " need an adjustment, but skipped due to in maintenance state", cllId);
 
 141                     post(new SimpleEvent(SimpleEvent.Type.AAI_BW_REQ, cllId));
 
 144                 //not in the mid of adjustment; we are free to adjust.
 
 145                 log.info("CCVPN Evaluator Output: candidate {}," +
 
 146                     " need an adjustment, sending request to policy", entry.getKey());
 
 147                 ccvpnPmDatastore.updateSvcState(entry.getKey(), ServiceState.UNDER_MAINTENANCE);
 
 148                 sendModifyRequest(entry.getKey(), newBw, RequestOwner.DCAE);
 
 150             log.debug("=== Processing periodic check complete ===");
 
 152         if (event.type() == SimpleEvent.Type.ONDEMAND_CHECK && isOnDemandCheckOn()) {
 
 153             log.info("=== Processing upperbound adjustment request: {} ===", event.time());
 
 154             JsonObject payload = (JsonObject) event.subject();
 
 155             String serviceId = payload.get(SERVICE_INSTANCE_LOCATION_ID).getAsString();
 
 156             int newBandwidth = payload.get(BANDWIDTH_TOTAL).getAsInt();
 
 157             log.info("Update service {} bandwidth upperbound to {} ", serviceId, newBandwidth);
 
 158             ccvpnPmDatastore.updateUpperBoundBw(serviceId, newBandwidth);
 
 159             log.info("=== Processing upperbound adjustment complete ===");
 
 164     public String getName() {
 
 169      * Post/broadcast event to the BandwidthEvaluator
 
 170      * @param event event object
 
 172     private void post(Event event){
 
 173         bandwidthEvaluator.post(event);
 
 176     private void loadConfig() {
 
 177         configuration = Configuration.getInstance();
 
 178         upperThreshold = configuration.getCcvpnEvalUpperThreshold();
 
 179         precision = configuration.getCcvpnEvalPrecision(); // in Mbps;
 
 182     private boolean isPeriodicCheckOn() {
 
 183         configuration = Configuration.getInstance();
 
 184         return configuration.isCcvpnEvalPeriodicCheckOn();
 
 187     private boolean isOnDemandCheckOn() {
 
 188         configuration = Configuration.getInstance();
 
 189         return configuration.isCcvpnEvalOnDemandCheckOn();
 
 192     // send modification requestion
 
 193     private void sendModifyRequest(String cllId, Integer newBandwidth, RequestOwner owner) {
 
 194         log.info("Sending modification request to policy. RequestOwner: {} - Service: {} change to bw: {}",
 
 195                 owner, cllId, newBandwidth);
 
 196         policyService.sendOnsetMessageToPolicy(
 
 197                 policyService.formPolicyOnsetMessageForCCVPN(cllId, newBandwidth, owner)
 
 200     // check if an adjustment is necessary
 
 201     private boolean needAdjust(String serivceId, double used, int provBandwidth, int upper){
 
 202         log.info("CCVPN Service Usage Analysis: usage: {}, threshold: {}, currentProvisioned {}, upperbound {}",
 
 203             used, upperThreshold, provBandwidth, upper);
 
 204         return provBandwidth > upper || used > upperThreshold * provBandwidth;
 
 207     // calculate new bandwidth to accomodate customer
 
 208     private int needAdjustTo(String serivceId, double used, int cur, int upper){
 
 212         int expected = (int) (Math.ceil((used / upperThreshold) * 1.2 / precision) * precision);
 
 213         return Math.min(expected, upper);
 
 215     // check is service under maint
 
 216     private boolean isServiceUnderMaintenance(String serivceId) {
 
 217         return ccvpnPmDatastore.getStatusOfSvc(serivceId) == ServiceState.UNDER_MAINTENANCE;
 
 219     // get a collection of service under maint
 
 220     private Map<String, ServiceState> getServicesUnderMaintenance(){
 
 221         return ccvpnPmDatastore.getSvcStatusMap().entrySet()
 
 223                 .filter(e -> e.getValue() == ServiceState.UNDER_MAINTENANCE)
 
 224                 .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));