package cloudsimulator.controllersimulator;

import cloudsimulator.clustersimulator.ClusterManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Implements auto scaling policies for the Cluster.
 */
@Component
public class AutoClusterScale {

    @Autowired
    ClusterFormationController clusterFormationController;

    private int upperUtilisationFactor = 70;
    private int lowerUtilisationFactor = 60;
    private int timeout = 0;
    private int upperThresholdExceedSuccession = 0;
    private int lowerThresholdExceedSuccession = 0;
    private long requestInLastSeconds;
    private long lowerThresholdRequestsInLastSecconds;

    /**
     * Decide to scale when the threshold is exceeded for multiple times in a row
     * and when there is no timeout from the last decision to scale.
     *
     * @param clusterManager
     * @param requestInLastSecond
     */
    public void scalePolicy(ClusterManager clusterManager, long requestInLastSecond) {
        long rpsForOneVm  = clusterManager.getRpsForOneVm();
        long maxRps =  clusterManager.computeMaxRps();

        long upperThreshold = upperUtilisationFactor * maxRps / 100;
        long lowerThreshold = lowerUtilisationFactor * maxRps / 100;

        // If the upperThreshold is exceeded multiple times in a row, the upperThresholdExceedSuccession will increment.
        if (requestInLastSecond > upperThreshold) {
            upperThresholdExceedSuccession++;
            requestInLastSeconds += requestInLastSecond;
        }
        else {
            upperThresholdExceedSuccession = 0;
            requestInLastSeconds = 0;
        }

        // if the lower threshold is exceeded multiple times, increse the lowerThresholdExceedSuccession.
        if (requestInLastSecond < lowerThreshold) {
            lowerThresholdExceedSuccession++;
            lowerThresholdRequestsInLastSecconds += requestInLastSecond;
        }
        else {
            lowerThresholdExceedSuccession = 0;
            lowerThresholdRequestsInLastSecconds = 0;
        }

        // Decrease timeout if it is set.
        if (timeout > 0) {
            timeout--;
        }

        // Decide how many VMs should be allocated.
        if (upperThresholdExceedSuccession >= 3 && timeout == 0) {
            long numberOfVmToAllocate = computeNumberOfVmToAllocate(requestInLastSeconds / upperThresholdExceedSuccession, upperThreshold, rpsForOneVm);
            clusterFormationController.allocateVMs(numberOfVmToAllocate, clusterManager);

            timeout = 60;
            upperThresholdExceedSuccession = 0;
            requestInLastSeconds = 0;
        }

        // Decide how many VMs to remove
        if (lowerThresholdExceedSuccession >= 3 && timeout == 0) {
            long numberOfVmToRemove = computeNumberOfVmToRemove(requestInLastSeconds / lowerThresholdRequestsInLastSecconds, lowerThreshold, rpsForOneVm);
            clusterFormationController.removeVMs(numberOfVmToRemove, clusterManager);

            timeout = 60;
            lowerThresholdExceedSuccession = 0;
            lowerThresholdRequestsInLastSecconds = 0;
        }

    }

    // return the number of VMs that should be allocated to keep the resources utilisation < 70%.
    private long computeNumberOfVmToAllocate(long averageRequestRateInLastSeconds, long upperThreshold, long rpsForOneVm) {
        long utilisationForFutureAllocationOfVms = rpsForOneVm * upperUtilisationFactor / 100;
        long requestsOverTheThreshold = averageRequestRateInLastSeconds - upperThreshold;
        return requestsOverTheThreshold / utilisationForFutureAllocationOfVms + 1;
    }


    private long computeNumberOfVmToRemove(long averageRequestRateInLastSeconds, long lowerThreshold, long rpsForOneVm) {
        long utilisationForFutureVmDeallocation = rpsForOneVm * lowerUtilisationFactor / 100;
        long requestsUnderTheThreshold = lowerThreshold - averageRequestRateInLastSeconds;
        return requestsUnderTheThreshold / utilisationForFutureVmDeallocation + 1;
    }

}

@@ -1749,16 +1749,17 @@
 s, incre
+a
 se the l
@@ -2102,50 +2102,8 @@
  %7D%0A%0A
-        // Decrease timeout if it is set.%0A
     
@@ -2174,47 +2174,81 @@
  // 
-Decide how many VMs 
+Allocate VMs if the upper thre
 sho
-u
 ld 
-be allocated
+was reached several consecutive times
 .%0A  
@@ -2694,37 +2694,68 @@
  // 
-Decide how many VMs to remove
+Remove VMs if the lower threshold was reached several times.
 %0A   
@@ -3215,19 +3215,24 @@
    /
-/ return th
+**%0A     * Comput
 e nu
@@ -3280,40 +3280,376 @@
 eep 
-the resources utilisation %3C 70%25.
+resources utilisation bellow the upper threshold.%0A     *%0A     * @param averageRequestRateInLastSeconds the average request rate in the last seconds.%0A     * @param upperThreshold is used to trigger VM allocation.%0A     * @param rpsForOneVm the maximum number of requests per second that a VM can handle.%0A     * @return the number of VMs that should be allocated.%0A     */
 %0A   
@@ -4045,16 +4045,447 @@
     %7D%0A%0A%0A
+    /**%0A     * Compute number of VM that should be stopped because the lower threshold was reached.%0A     *%0A     * @param averageRequestRateInLastSeconds the average request rate in the last seconds.%0A     * @param lowerThreshold is used to trigger removal of VMs.%0A     * @param rpsForOneVm the maximum number of requests per second that a VM can handle.%0A     * @return number of VMs that should be removed from the cluster.%0A     */%0A
     priv

Add comments.