br.com.autonomiccs.autonomic.administration.algorithms.impl.VmsDispersionAlgorithmForHomogeneousEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for br.com.autonomiccs.autonomic.administration.algorithms.impl.VmsDispersionAlgorithmForHomogeneousEnvironment.java

Source

/*
 * This program is part of Autonomiccs "autonomic-platform",
 * an open source autonomic cloud computing management platform.
 * Copyright (C) 2016 Autonomiccs, Inc.
 *
 * Licensed to the Autonomiccs, Inc. under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The Autonomiccs, Inc. licenses this file
 * to you 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.
 */
package br.com.autonomiccs.autonomic.administration.algorithms.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math3.util.MathUtils;

import br.com.autonomiccs.autonomic.algorithms.commons.beans.CloudResources;
import br.com.autonomiccs.autonomic.algorithms.commons.beans.HostResources;
import br.com.autonomiccs.autonomic.algorithms.commons.beans.VmResources;

/**
 * This class provides heuristics that allows the manager to balance the cluster workload among its
 * hosts. This heuristic does not power off hosts, its actions are related to VMs migrations.
 */
public class VmsDispersionAlgorithmForHomogeneousEnvironment extends ConsolidationScoredPreferenceForSmallHosts {

    /**
     * Represents the sum of hosts used memory divided by the sum of hosts total memory.
     */
    private double clusterMemoryUsageAverage;

    /**
     * Standard deviation of VMs memory configuration. This value considers scenarios where there
     * are a high variability in the VMs amount of resources, given the maximum and minimum load
     * allowed considering that some VMs might be big.
     */
    private double standardDeviationVmsConfiguration;

    /**
     * Standard deviation of hosts memory usage. This value considers the heterogeneous usage of
     * resources in the hosts, if the usage is very different, it will have a higher standard
     * deviation and allows the maximum and minimum load based on this evaluation.
     */
    private double standardDeviationHostsUsage;

    /**
     * It is the average between the {@link #standardDeviationVmsConfiguration} and
     * {@link #standardDeviationHostsUsage} standard deviation values.
     */
    private double standardDeviationAverage;

    private StandardDeviation std = new StandardDeviation(false);

    public VmsDispersionAlgorithmForHomogeneousEnvironment() {
        canHeuristicShutdownHost = false;
    }

    /**
     * It ranks hosts to receive VMs. It clones the given list (using
     * {@link #cloneListOfHosts(List)}),
     * sets each host score using {@link #setEachHostScore(List)} and sorts the list using
     * {@link #sortHosts(List)} method.
     */
    @Override
    public List<HostResources> rankHosts(List<HostResources> hostsList) {
        clusterMemoryUsagePercentage(hostsList);
        List<HostResources> sortedHosts = cloneListOfHosts(hostsList);
        setEachHostScore(sortedHosts);
        sortHosts(sortedHosts);
        return sortedHosts;
    }

    /**
     * It sorts hosts downward using the {@link #sortHostsUpwardScore(List)} method.
     */
    @Override
    protected void sortHosts(List<HostResources> hosts) {
        sortHostsDownwardScore(hosts);
    }

    /**
     * It clones the given list using the {@link #cloneListOfHosts(List)} method
     */
    @Override
    public List<HostResources> rankHostToPowerOff(List<HostResources> idleHosts) {
        return cloneListOfHosts(idleHosts);
    }

    /**
     * It calculates the host score based on its memory usage proportion (memory usage / total
     * memory)
     */
    @Override
    protected double calculateHostScore(HostResources host) {
        if (host.getUsedMemoryInMegaBytes() != 0) {
            return (host.getTotalMemoryInBytes() / BYTES_TO_MEGA_BYTES) / host.getUsedMemoryInMegaBytes();
        }
        return Double.POSITIVE_INFINITY;
    }

    /**
     * It returns false to ensure that this heuristic does not shut down any host.
     */
    @Override
    public boolean internalCanPowerOffHost(HostResources hostToPowerOff, CloudResources cloudResources) {
        return false;
    }

    /**
     * It updates the cluster memory usage average, and the standard deviations (
     * {@link #clusterMemoryUsageAverage}, {@link #standardDeviationAverage},
     * {@link #standardDeviationHostsUsage}, {@link #standardDeviationVmsConfiguration})
     */
    protected void clusterMemoryUsagePercentage(List<HostResources> hosts) {
        double clusterUsedMemory = 0;
        double vmsUsedMemory[] = null;
        double hostsUsedMemory[] = new double[hosts.size()];
        for (int hostIterator = 0; hostIterator < hosts.size(); hostIterator++) {
            List<VmResources> vmsResources = hosts.get(hostIterator).getVmsResources();
            double hostVmsUsedMemory[] = new double[vmsResources.size()];
            for (int vmIterator = 0; vmIterator < vmsResources.size(); vmIterator++) {
                hostVmsUsedMemory[vmIterator] = vmsResources.get(vmIterator).getMemoryInMegaBytes();
            }
            vmsUsedMemory = ArrayUtils.addAll(vmsUsedMemory, hostVmsUsedMemory);

            long hostUsedMemoryInMegaBytes = hosts.get(hostIterator).getUsedMemoryInMegaBytes();
            hostsUsedMemory[hostIterator] = hostUsedMemoryInMegaBytes;
            clusterUsedMemory += hostUsedMemoryInMegaBytes;
        }
        clusterMemoryUsageAverage = clusterUsedMemory / hosts.size();
        standardDeviationVmsConfiguration = std.evaluate(vmsUsedMemory);
        standardDeviationHostsUsage = std.evaluate(hostsUsedMemory);
        standardDeviationAverage = (standardDeviationVmsConfiguration + standardDeviationHostsUsage) / 2;
    }

    /**
     * It maps VMs from hosts with lower score (interest in distribute some of its VMs) to hosts
     * with
     * higher score (interesting to allocate more VMs). This methods simulates three different
     * mappings methods; the simulation that give the best standard deviation on the hosts workload
     * will be the chosen one. Each simulation is done by calling the {@link simulateVmsMigrations}
     * method with a different standard deviation ({@link #standardDeviationVmsConfiguration},
     * {@link #standardDeviationHostsUsage}, {@link #standardDeviationAverage}).
     * The standard deviation is used to give a maximum load in each host (host average load +
     * standard deviation) and the minimum load (host average load - standard deviation).
     */
    @Override
    public Map<Long, HostResources> mapVMsToHost(List<HostResources> rankedHosts) {
        if (MathUtils.equals(standardDeviationHostsUsage, 0)) {
            return new HashMap<>();
        }
        List<HostResources> rankedHostsStdVms = cloneListOfHosts(rankedHosts);
        List<HostResources> rankedHostsStdHosts = cloneListOfHosts(rankedHosts);
        List<HostResources> rankedHostsStdAverage = cloneListOfHosts(rankedHosts);

        logger.debug("Simulating migration mapping with VMs resource standard deviation");
        Map<Long, HostResources> vmsToHostStdVms = simulateVmsMigrations(rankedHostsStdVms,
                standardDeviationVmsConfiguration);

        logger.debug("Simulating migration mapping with hosts resource standard deviation");
        Map<Long, HostResources> vmsToHostStdHosts = simulateVmsMigrations(rankedHostsStdHosts,
                standardDeviationHostsUsage);

        logger.debug(
                "Simulating migration mapping with the average of hosts resource and VMs resource standard deviation");
        Map<Long, HostResources> vmsToHostStdAverage = simulateVmsMigrations(rankedHostsStdAverage,
                standardDeviationAverage);

        double usedMemoryHostsWithStdVms[] = new double[rankedHosts.size()];
        for (int i = 0; i < rankedHostsStdVms.size(); i++) {
            usedMemoryHostsWithStdVms[i] = rankedHostsStdVms.get(i).getUsedMemoryInMegaBytes();
        }
        double stdWithStdVms = std.evaluate(usedMemoryHostsWithStdVms);
        logger.debug(String.format(
                "The Std. achieved using the VMs resource standard deviation as parameter for the simulation was [%f]",
                stdWithStdVms));
        double usedMemoryHostsWithStdHost[] = new double[rankedHosts.size()];
        for (int i = 0; i < rankedHostsStdHosts.size(); i++) {
            usedMemoryHostsWithStdHost[i] = rankedHostsStdHosts.get(i).getUsedMemoryInMegaBytes();
        }
        double stdWithStdHosts = std.evaluate(usedMemoryHostsWithStdHost);
        logger.debug(String.format(
                "The Std. achieved using the hosts resource standard deviation as parameter for the simulation was [%f]",
                stdWithStdHosts));

        double usedMemoryHostsWithStdAverage[] = new double[rankedHosts.size()];
        for (int i = 0; i < rankedHostsStdAverage.size(); i++) {
            usedMemoryHostsWithStdAverage[i] = rankedHostsStdAverage.get(i).getUsedMemoryInMegaBytes();
        }
        double stdWithStdAverage = std.evaluate(usedMemoryHostsWithStdAverage);
        logger.debug(String.format(
                "The Std. achieved using the average between hosts resource and VMs resource standard deviation as parameter for the simulation was [%f]",
                stdWithStdAverage));

        if (stdWithStdAverage <= stdWithStdHosts && stdWithStdAverage <= stdWithStdVms) {
            logger.debug(
                    "The simulation that won the competition was the one executed with alpha as the average of host resource and VMs resource standard deviation.");
            logger.debug(String.format("The number of migrations that will be executed is[%d].",
                    vmsToHostStdAverage.size()));
            return vmsToHostStdAverage;
        }
        if (stdWithStdHosts <= stdWithStdVms) {
            logger.debug(
                    "The simulation that won the competition was the one executed with alpha as the host resource standard deviation.");
            logger.debug(String.format("The number of migrations that will be executed is[%d].",
                    vmsToHostStdHosts.size()));
            return vmsToHostStdHosts;
        }
        logger.debug(
                "The simulation that won the competition was the one executed with alpha as the VMs resource standard deviation.");
        logger.debug(
                String.format("The number of migrations that will be executed is[%d].", vmsToHostStdVms.size()));
        return vmsToHostStdVms;
    }

    /**
     * For a given a list of {@link HostResources} and a value that represents the standard
     * deviation it maps virtual machine migrations. The standar deviation is used as a
     */
    protected Map<Long, HostResources> simulateVmsMigrations(List<HostResources> rankedHosts,
            double standardDeviation) {
        Map<Long, HostResources> vmsToHost = new HashMap<>();

        double hostMemoryMinimumUsageAllowed = clusterMemoryUsageAverage - standardDeviation;
        for (int i = rankedHosts.size() - 1; i > 0; i--) {
            HostResources hostToBeOffLoaded = rankedHosts.get(i);
            if (hostToBeOffLoaded.getUsedMemoryInMegaBytes() > hostMemoryMinimumUsageAllowed) {

                for (HostResources hostCandidateToReceiveVMs : rankedHosts) {
                    if (hostCandidateToReceiveVMs.getUsedMemoryInMegaBytes() <= clusterMemoryUsageAverage) {

                        List<VmResources> clonedListOfVms = cloneHostVmsList(hostToBeOffLoaded);
                        for (VmResources vmResources : clonedListOfVms) {
                            if (isMemoryUsageOfHostsAfterVmMigration(vmResources, hostToBeOffLoaded,
                                    hostCandidateToReceiveVMs, standardDeviation)
                                    && canMigrateVmToHost(vmResources, hostCandidateToReceiveVMs)) {
                                updateHostUsedResources(vmResources, hostToBeOffLoaded, hostCandidateToReceiveVMs);
                                vmsToHost.put(vmResources.getVmId(), hostCandidateToReceiveVMs);
                            }
                        }
                    }
                }
            }
        }
        return vmsToHost;
    }

    /**
     * It updates the resources counting (used cpu and memory) from the host that the VM resids on
     * and the target host to be migrated.
     */
    private void updateHostUsedResources(VmResources vm, HostResources hostVmResidOn, HostResources targetHost) {
        super.updateHostUsedResources(vm, targetHost);
        targetHost.getVmsResources().add(vm);

        hostVmResidOn.getVmsResources().remove(vm);
        hostVmResidOn.setUsedCpu(hostVmResidOn.getUsedCpu() - (vm.getCpuSpeed() * vm.getNumberOfCpus()));
        hostVmResidOn
                .setUsedMemoryInMegaBytes(hostVmResidOn.getUsedMemoryInMegaBytes() - vm.getMemoryInMegaBytes());
    }

    /**
     * It returns true if a VM migration will maintain the VM's current host with a usage above the
     * {@link #hostMemoryMinimumUsageAllowedStdVms}; and if the target host usage will stay below
     * the {@link #hostMemoryMaximumUsageAllowedStdVms} after the migration.
     */
    private boolean isMemoryUsageOfHostsAfterVmMigration(VmResources vmResources, HostResources hostVmResidOn,
            HostResources targetHost, double standarDeviation) {
        if (hostVmResidOn.getUsedMemoryInMegaBytes() == targetHost.getUsedMemoryInMegaBytes()) {
            return false;
        }
        long vmMemoryInMegaBytes = vmResources.getMemoryInMegaBytes();
        long hostMemoryAfterMigrateVm = hostVmResidOn.getUsedMemoryInMegaBytes() - vmMemoryInMegaBytes;
        long targetHostMemoryUsageAfterReceiveVM = targetHost.getUsedMemoryInMegaBytes() + vmMemoryInMegaBytes;

        double hostMemoryMinimumUsageAllowed = clusterMemoryUsageAverage - standarDeviation;
        double hostMemoryMaximumUsageAllowed = clusterMemoryUsageAverage + standarDeviation;

        return hostMemoryMinimumUsageAllowed <= hostMemoryAfterMigrateVm
                && targetHostMemoryUsageAfterReceiveVM < hostMemoryMaximumUsageAllowed;
    }
}