/* * Copyright (c) 2010-2012 Thiago T. Sá * * This file is part of CloudReports. * * CloudReports is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CloudReports is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For more information about your rights as a user of CloudReports, * refer to the LICENSE file or see . */ package cloudreports.extensions.vmallocationpolicies; import cloudreports.business.SettingBusiness; import cloudreports.enums.AllocationPolicy; import cloudreports.models.Migration; import cloudreports.simulation.Simulation; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import org.cloudbus.cloudsim.Log; import org.cloudbus.cloudsim.Vm; import org.cloudbus.cloudsim.VmAllocationPolicySimple; import org.cloudbus.cloudsim.core.CloudSim; import org.cloudbus.cloudsim.power.PowerHost; /** * A virtual machine allocation policy based on a single utilization threshold. * * @see VmAllocationPolicy * @see AllocationPolicy#SINGLE_THRESHOLD * @author Thiago T. Sá * @since 1.0 */ public class VmAllocationPolicySingleThreshold extends VmAllocationPolicySimple implements VmAllocationPolicyExtensible { private double upperUtilizationThreshold; /** * Initializes a new instance of this class with the given list of hosts * and utilization threshold value. * * @param list the list of hosts. * @param utilizationThreshold the utilization threshold value. * @since 1.0 */ public VmAllocationPolicySingleThreshold(List list, double utilizationThreshold) { super(list); this.upperUtilizationThreshold = utilizationThreshold; } /** * Optimizes the allocation of virtual machines. * It distributes virtual machines if there is any overused host and then * consolidates the allocation. * * @param vmList the list of virtual machines. * @since 1.0 */ @Override public List getListOfMigrationsToBeExecuted(List vmList) { List migrationList = new ArrayList(); if (vmList.isEmpty()) { return migrationList; } //Verify the existence of overused hosts List overusedHosts = getOverusedHosts(); //If there is any overused host, then distribute vms if(!overusedHosts.isEmpty()) { distributeVms(migrationList, overusedHosts); } consolidateVms(migrationList); return migrationList; } /** * Distributes virtual machines among hosts. * * @param migrationList the list of migrations to be executed. * @param overusedHosts the list of overused hosts. * @since 1.0 */ public void distributeVms(List migrationList, List overusedHosts) { List targetHosts = getNotOverusedHosts(); if(!targetHosts.isEmpty()) { sortByPowerConsumption(overusedHosts); for(PowerHost host : overusedHosts) { getDistributingMigrationList(migrationList, host, targetHosts, getUpperUtilizationThreshold()); } for(Migration migration : migrationList) { migration.setDescription("Distribution"); } } } /** * Consolidates the allocation of virtual machines. * * @param migrationList the list of migrations to be executed. * @since 1.0 */ public void consolidateVms(List migrationList) { List activeHosts = getActiveHosts(); List targetHosts = activeHosts; List sourceHosts = activeHosts; List hostsToBeTurnedOffIds = new ArrayList(); for (PowerHost sourceHost : sourceHosts) { List newMigrationList = new ArrayList(); getConsolidatingMigrationList(newMigrationList, sourceHost, targetHosts, hostsToBeTurnedOffIds, getUpperUtilizationThreshold()); migrationList.addAll(newMigrationList); hostsToBeTurnedOffIds.add(sourceHost.getId()); } for (Migration migration : migrationList) { if(migration.getDescription() == null) { migration.setDescription("Consolidation"); } } } public double getUpperUtilizationThreshold() { return this.upperUtilizationThreshold; } /** * Gets a list of overused hosts. * * @return the list of overused hosts. * @since 1.0 */ public List getOverusedHosts() { List overusedHosts = new ArrayList(); for (PowerHost host : this.getHostList()) { double cpuUtilizationRate = getHostCpuUtilizationRate(host); double ramUtilizationRate = getHostRamUtilizationRate(host); if (cpuUtilizationRate >= getUpperUtilizationThreshold() || ramUtilizationRate >= getUpperUtilizationThreshold()) { overusedHosts.add(host); } } return overusedHosts; } /** * Gets a list of not overused hosts. * * @return the list of not overused hosts. * @since 1.0 */ public List getNotOverusedHosts() { List notOverusedHosts = new ArrayList(); for (PowerHost host : this.getHostList()) { double cpuUtilizationRate = getHostCpuUtilizationRate(host); double ramUtilizationRate = getHostRamUtilizationRate(host); if ( cpuUtilizationRate < getUpperUtilizationThreshold() && ramUtilizationRate < getUpperUtilizationThreshold()) { notOverusedHosts.add(host); } } return notOverusedHosts; } /** * Gets a list active hosts. * * @return the list of active hosts. * @since 1.0 */ public List getActiveHosts() { List activeHosts = new ArrayList(); for (PowerHost host : this.getHostList()) { double cpuUtilization = host.getUtilizationOfCpuMips()/host.getTotalMips(); double ramUtilization = host.getUtilizationOfRam()/host.getRam(); if ( cpuUtilization > 0 && ramUtilization > 0) { activeHosts.add(host); } } return activeHosts; } /** * Allocates a host for a given VM. * * @param vm a virtual machine. * * @return true if the host could be allocated; * false otherwise. */ @Override public boolean allocateHostForVm(Vm vm) { PowerHost allocatedHost = findHostForVm(vm); if (allocatedHost != null && allocatedHost.vmCreate(vm)) { //if vm has been succesfully created in the host getVmTable().put(vm.getUid(), allocatedHost); if (!Log.isDisabled()) { Log.print(String.format("%.2f: VM #" + vm.getId() + " has been allocated to the host #" + allocatedHost.getId() + "\n", CloudSim.clock())); } return true; } return false; } /** * Finds a host to allocate for the VM. * * @param vm the virtual machine to be allocated. * @return the chosen host. */ public PowerHost findHostForVm(Vm vm) { double minPower = Double.MAX_VALUE; PowerHost allocatedHost = null; for (PowerHost host : this.getHostList()) { if (host.isSuitableForVm(vm)) { double maxUtilization = getMaxUtilizationAfterAllocation(host, vm); if ((!vm.isBeingInstantiated() && maxUtilization > getUpperUtilizationThreshold()) || (vm.isBeingInstantiated() && maxUtilization > 1.0)) { continue; } try { double powerAfterAllocation = getPowerAfterAllocation(host, vm); if (powerAfterAllocation != -1) { double powerDiff = powerAfterAllocation - host.getPower(); if (powerDiff < minPower) { minPower = powerDiff; allocatedHost = host; } } } catch (Exception e) { } } } return allocatedHost; } /** * Gets the power after allocation. * * @param host the host. * @param vm the virtual machine. * @return the power usage after allocation of the virtual machine. */ protected double getPowerAfterAllocation(PowerHost host, Vm vm) { List allocatedMipsForVm = null; PowerHost allocatedHost = (PowerHost) vm.getHost(); if (allocatedHost != null) { allocatedMipsForVm = allocatedHost.getAllocatedMipsForVm(vm); } if (!host.allocatePesForVm(vm, vm.getCurrentRequestedMips())) { return -1; } double power = host.getPower(); host.deallocatePesForVm(vm); if (allocatedHost != null && allocatedMipsForVm != null) { vm.getHost().allocatePesForVm(vm, allocatedMipsForVm); } return power; } /** * Gets the maximum utilization after allocation. * * @param host the host. * @param vm the virtual machine. * @return the maximum utilization after allocation of the virtual machine. */ protected double getMaxUtilizationAfterAllocation(PowerHost host, Vm vm) { List allocatedMipsForVm = null; PowerHost allocatedHost = (PowerHost) vm.getHost(); if (allocatedHost != null) { allocatedMipsForVm = vm.getHost().getAllocatedMipsForVm(vm); } if (!host.allocatePesForVm(vm, vm.getCurrentRequestedMips())) { return -1; } double maxUtilization = host.getMaxUtilizationAmongVmsPes(vm); host.deallocatePesForVm(vm); if (allocatedHost != null && allocatedMipsForVm != null) { vm.getHost().allocatePesForVm(vm, allocatedMipsForVm); } return maxUtilization; } /** * Deallocates a virtual machine in a host. * * @param vm the virtual machine to be deallocated. */ @Override public void deallocateHostForVm(Vm vm) { if (getVmTable().containsKey(vm.getUid())) { PowerHost host = (PowerHost) getVmTable().remove(vm.getUid()); if (host != null) { host.vmDestroy(vm); } } } /** * Sorts hosts by power consumption. * * @param hostList a list of hosts to be sorted. * @since 1.0 */ protected void sortByPowerConsumption(List hostList) { Collections.sort(hostList, new Comparator() { @Override public int compare(T a, T b) throws ClassCastException { Double aUtilization = a.getPower(); Double bUtilization = b.getPower(); return bUtilization.compareTo(aUtilization); } }); } /** * Calculates a list of migrations needed to distribute virtual machines * among hosts. * * @param migrationList a list o migrations to be calculated. * @param sourceHost the host from which virtual machines will be taken to * be reallocated in other hosts. * @param targetHosts a list of target hosts. * @param upperUtilizationThreshold the upper utilization threshold. * @since 1.0 */ protected void getDistributingMigrationList(List migrationList, PowerHost sourceHost, List targetHosts, double upperUtilizationThreshold) { //Map of used resources on target hosts //Keys: the target hosts IDs //Values: a list with the amount of used CPU and RAM on the target host HashMap> resourcesMap = new HashMap>(); for (PowerHost host : targetHosts) { List resourcesList = new ArrayList(); resourcesList.add(getHostCpuUtilization(host)); resourcesList.add(getHostRamUtilization(host)); resourcesMap.put(host.getId(), resourcesList); } double cpuUtilization = getHostCpuUtilization(sourceHost); double ramUtilization = getHostRamUtilization(sourceHost); int currentSimulation = SettingBusiness.getCurrentSimulation(); VMS_LOOP: for (Vm vm : sourceHost.getVmList()) { if (vm.isInMigration()) { continue; } HOSTS_LOOP: for (PowerHost targetHost : targetHosts) { List hostResources = resourcesMap.get(targetHost.getId()); double vmTotalMips = vm.getNumberOfPes() * vm.getMips(); if ((hostResources.get(0) + vmTotalMips) / targetHost.getTotalMips() < upperUtilizationThreshold && (hostResources.get(1) + vm.getRam()) / targetHost.getRam() < upperUtilizationThreshold) { //There's enough resources and no threshold violation, so add the migration to the list. //Step 1: add the resources utilization of the target host hostResources.set(0, hostResources.get(0) + vmTotalMips); hostResources.set(1, hostResources.get(1) + vm.getRam()); //Step 2: subtract the utilization of the source host ramUtilization -= vm.getRam(); cpuUtilization -= sourceHost.getTotalAllocatedMipsForVm(vm); //Step 3: add the entry on the migration map Migration migration = new Migration(this, targetHost, sourceHost, vm, currentSimulation); migrationList.add(migration); //If the source host is not overused anymore, so finish distribution. if ((ramUtilization / sourceHost.getRam()) < upperUtilizationThreshold && (cpuUtilization / sourceHost.getTotalMips()) < upperUtilizationThreshold) { break VMS_LOOP; } else { break HOSTS_LOOP; } } } } } /** * Calculates a list of migrations needed to consolidate the allocation of * virtual machines. * * @param migrationList a list o migrations to be calculated. * @param sourceHost the host from which virtual machines will be taken to * be reallocated in other hosts. * @param targetHosts a list of target hosts. * @param hostsToBeTurnedOffIds a list of host ids to be turned off. * @param upperUtilizationThreshold the upper utilization threshold. * @since 1.0 */ protected void getConsolidatingMigrationList(List migrationList, PowerHost sourceHost, List targetHosts, List hostsToBeTurnedOffIds, double upperUtilizationThreshold) { //Map of resources //Keys: each one of the target hosts ID //Values: a list with the amount of available CPU and RAM of the host HashMap> resourcesMap = new HashMap>(); for (PowerHost host : targetHosts) { List resourcesList = new ArrayList(); resourcesList.add(getHostCpuUtilization(host)); resourcesList.add(getHostRamUtilization(host)); resourcesMap.put(host.getId(), resourcesList); } double cpuUtilization = getHostCpuUtilization(sourceHost); double ramUtilization = getHostRamUtilization(sourceHost); int currentSimulation = SettingBusiness.getCurrentSimulation(); for (Vm vm : sourceHost.getVmList()) { if (vm.isInMigration()) { continue; } HOSTS_LOOP: for (PowerHost targetHost : targetHosts) { if (targetHost.getId() == sourceHost.getId()) { continue HOSTS_LOOP; } for (int id : hostsToBeTurnedOffIds) { if (targetHost.getId() == id) { continue HOSTS_LOOP; } } List hostResources = resourcesMap.get(targetHost.getId()); double vmTotalMips = vm.getNumberOfPes() * vm.getMips(); if ((hostResources.get(0) + vmTotalMips) / targetHost.getTotalMips() < upperUtilizationThreshold && (hostResources.get(1) + vm.getRam()) / targetHost.getRam() < upperUtilizationThreshold) { //There's enough resources and no threshold violation, so add the migration to the list. //Step 1: subtract the resources of the target host hostResources.set(0, hostResources.get(0) + vmTotalMips); hostResources.set(1, hostResources.get(1) + vm.getRam()); //Step 2: subtract the utilization of the source host ramUtilization -= vm.getRam(); cpuUtilization -= sourceHost.getTotalAllocatedMipsForVm(vm); //Step 3: add the entry to the migration map Migration migration = new Migration(this, targetHost, sourceHost, vm, currentSimulation); migrationList.add(migration); break HOSTS_LOOP; } } } //The consolidation will only occur if all vms have been migrated if (ramUtilization > 0 || cpuUtilization > 0) { migrationList.clear(); } } /** * Gets the CPU utilization of a host. * * @param host the host to be analyzed. * @since 1.0 */ protected static double getHostCpuUtilization(PowerHost host) { double cpuUtilization = host.getUtilizationOfCpuMips(); //Disconsider the resources of vms migrating out for (Vm vm : host.getVmList()) { if (vm.isInMigration()) { cpuUtilization -= host.getTotalAllocatedMipsForVm(vm); } } //Include resources of vms migrating in for (Vm vm : host.getVmsMigratingIn()) { if (vm.isInMigration()) { cpuUtilization += vm.getCurrentRequestedTotalMips(); } } return cpuUtilization; } /** * Gets the RAM utilization of a host. * * @param host the host to be analyzed. * @since 1.0 */ protected static double getHostRamUtilization(PowerHost host) { double ramUtilization = host.getUtilizationOfRam(); //Disconsider the resources of vms migrating out for (Vm vm : host.getVmList()) { if (vm.isInMigration()) { ramUtilization -= vm.getRam(); } } //Include resources of vms migrating in for (Vm vm : host.getVmsMigratingIn()) { if (vm.isInMigration()) { ramUtilization += vm.getRam(); } } return ramUtilization; } /** * Gets the CPU utilization rate of a host. * * @param host the host to be analyzed. * @since 1.0 */ public static double getHostCpuUtilizationRate(PowerHost host) { double cpuUtilizationRate = getHostCpuUtilization(host) / host.getTotalMips(); List previousValuesCpu = Simulation.getDataCollector().getMonitoredUsedResources(host.getDatacenter().getName(), host.getId(), "CPU"); for (Double previousValue : previousValuesCpu) { cpuUtilizationRate += (previousValue / 100); } cpuUtilizationRate /= (previousValuesCpu.size() + 1); return cpuUtilizationRate; } /** * Gets the RAM utilization rate of a host. * * @param host the host to be analyzed. * @since 1.0 */ public static double getHostRamUtilizationRate(PowerHost host) { double ramUtilizationRate = getHostRamUtilization(host) / host.getRam(); List previousValuesRam = Simulation.getDataCollector().getMonitoredUsedResources(host.getDatacenter().getName(), host.getId(), "RAM"); for (Double previousValue : previousValuesRam) { ramUtilizationRate += (previousValue / 100); } ramUtilizationRate /= (previousValuesRam.size() + 1); return ramUtilizationRate; } }