/*
* Title: CloudSim Toolkit
* Description: CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation of Clouds
* Licence: GPL - http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2009-2012, The University of Melbourne, Australia
*/
package org.cloudbus.cloudsim.power;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.cloudbus.cloudsim.Host;
import org.cloudbus.cloudsim.HostDynamicWorkload;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.power.lists.PowerVmList;
import org.cloudbus.cloudsim.util.ExecutionTimeMeasurer;
/**
* An abstract power-aware VM allocation policy that dynamically optimizes the VM
* allocation (placement) using migration.
*
* <br/>If you are using any algorithms, policies or workload included in the power package please cite
* the following paper:<br/>
*
* <ul>
* <li><a href="http://dx.doi.org/10.1002/cpe.1867">Anton Beloglazov, and Rajkumar Buyya, "Optimal Online Deterministic Algorithms and Adaptive
* Heuristics for Energy and Performance Efficient Dynamic Consolidation of Virtual Machines in
* Cloud Data Centers", Concurrency and Computation: Practice and Experience (CCPE), Volume 24,
* Issue 13, Pages: 1397-1420, John Wiley & Sons, Ltd, New York, USA, 2012</a>
* </ul>
*
* @author Anton Beloglazov
* @since CloudSim Toolkit 3.0
*/
public abstract class PowerVmAllocationPolicyMigrationAbstract extends PowerVmAllocationPolicyAbstract {
/** The vm selection policy. */
private PowerVmSelectionPolicy vmSelectionPolicy;
/** A list of maps between a VM and the host where it is place.
* @todo This list of map is implemented in the worst way.
* It should be used just a Map<Vm, Host> to find out
* what PM is hosting a given VM.
*/
private final List<Map<String, Object>> savedAllocation = new ArrayList<Map<String, Object>>();
/** A map of CPU utilization history (in percentage) for each host,
where each key is a host id and each value is the CPU utilization percentage history.*/
private final Map<Integer, List<Double>> utilizationHistory = new HashMap<Integer, List<Double>>();
/**
* The metric history.
* @todo the map stores different data. Sometimes it stores the upper threshold,
* other it stores utilization threshold or predicted utilization, that
* is very confusing.
*/
private final Map<Integer, List<Double>> metricHistory = new HashMap<Integer, List<Double>>();
/** The time when entries in each history list was added.
* All history lists are updated at the same time.
*/
private final Map<Integer, List<Double>> timeHistory = new HashMap<Integer, List<Double>>();
/** The history of time spent in VM selection
* every time the optimization of VM allocation method is called.
* @see #optimizeAllocation(java.util.List)
*/
private final List<Double> executionTimeHistoryVmSelection = new LinkedList<Double>();
/** The history of time spent in host selection
* every time the optimization of VM allocation method is called.
* @see #optimizeAllocation(java.util.List)
*/
private final List<Double> executionTimeHistoryHostSelection = new LinkedList<Double>();
/** The history of time spent in VM reallocation
* every time the optimization of VM allocation method is called.
* @see #optimizeAllocation(java.util.List)
*/
private final List<Double> executionTimeHistoryVmReallocation = new LinkedList<Double>();
/** The history of total time spent in every call of the
* optimization of VM allocation method.
* @see #optimizeAllocation(java.util.List)
*/
private final List<Double> executionTimeHistoryTotal = new LinkedList<Double>();
/**
* Instantiates a new PowerVmAllocationPolicyMigrationAbstract.
*
* @param hostList the host list
* @param vmSelectionPolicy the vm selection policy
*/
public PowerVmAllocationPolicyMigrationAbstract(
List<? extends Host> hostList,
PowerVmSelectionPolicy vmSelectionPolicy) {
super(hostList);
setVmSelectionPolicy(vmSelectionPolicy);
}
/**
* Optimize allocation of the VMs according to current utilization.
*
* @param vmList the vm list
*
* @return the array list< hash map< string, object>>
*/
@Override
public List<Map<String, Object>> optimizeAllocation(List<? extends Vm> vmList) {
ExecutionTimeMeasurer.start("optimizeAllocationTotal");
ExecutionTimeMeasurer.start("optimizeAllocationHostSelection");
List<PowerHostUtilizationHistory> overUtilizedHosts = getOverUtilizedHosts();
getExecutionTimeHistoryHostSelection().add(
ExecutionTimeMeasurer.end("optimizeAllocationHostSelection"));
printOverUtilizedHosts(overUtilizedHosts);
saveAllocation();
ExecutionTimeMeasurer.start("optimizeAllocationVmSelection");
List<? extends Vm> vmsToMigrate = getVmsToMigrateFromHosts(overUtilizedHosts);
getExecutionTimeHistoryVmSelection().add(ExecutionTimeMeasurer.end("optimizeAllocationVmSelection"));
Log.printLine("Reallocation of VMs from the over-utilized hosts:");
ExecutionTimeMeasurer.start("optimizeAllocationVmReallocation");
List<Map<String, Object>> migrationMap = getNewVmPlacement(vmsToMigrate, new HashSet<Host>(
overUtilizedHosts));
getExecutionTimeHistoryVmReallocation().add(
ExecutionTimeMeasurer.end("optimizeAllocationVmReallocation"));
Log.printLine();
migrationMap.addAll(getMigrationMapFromUnderUtilizedHosts(overUtilizedHosts));
restoreAllocation();
getExecutionTimeHistoryTotal().add(ExecutionTimeMeasurer.end("optimizeAllocationTotal"));
return migrationMap;
}
/**
* Gets the migration map from under utilized hosts.
*
* @param overUtilizedHosts the over utilized hosts
* @return the migration map from under utilized hosts
*/
protected List<Map<String, Object>> getMigrationMapFromUnderUtilizedHosts(
List<PowerHostUtilizationHistory> overUtilizedHosts) {
List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
List<PowerHost> switchedOffHosts = getSwitchedOffHosts();
// over-utilized hosts + hosts that are selected to migrate VMs to from over-utilized hosts
Set<PowerHost> excludedHostsForFindingUnderUtilizedHost = new HashSet<PowerHost>();
excludedHostsForFindingUnderUtilizedHost.addAll(overUtilizedHosts);
excludedHostsForFindingUnderUtilizedHost.addAll(switchedOffHosts);
excludedHostsForFindingUnderUtilizedHost.addAll(extractHostListFromMigrationMap(migrationMap));
// over-utilized + under-utilized hosts
Set<PowerHost> excludedHostsForFindingNewVmPlacement = new HashSet<PowerHost>();
excludedHostsForFindingNewVmPlacement.addAll(overUtilizedHosts);
excludedHostsForFindingNewVmPlacement.addAll(switchedOffHosts);
int numberOfHosts = getHostList().size();
while (true) {
if (numberOfHosts == excludedHostsForFindingUnderUtilizedHost.size()) {
break;
}
PowerHost underUtilizedHost = getUnderUtilizedHost(excludedHostsForFindingUnderUtilizedHost);
if (underUtilizedHost == null) {
break;
}
Log.printConcatLine("Under-utilized host: host #", underUtilizedHost.getId(), "\n");
excludedHostsForFindingUnderUtilizedHost.add(underUtilizedHost);
excludedHostsForFindingNewVmPlacement.add(underUtilizedHost);
List<? extends Vm> vmsToMigrateFromUnderUtilizedHost = getVmsToMigrateFromUnderUtilizedHost(underUtilizedHost);
if (vmsToMigrateFromUnderUtilizedHost.isEmpty()) {
continue;
}
Log.print("Reallocation of VMs from the under-utilized host: ");
if (!Log.isDisabled()) {
for (Vm vm : vmsToMigrateFromUnderUtilizedHost) {
Log.print(vm.getId() + " ");
}
}
Log.printLine();
List<Map<String, Object>> newVmPlacement = getNewVmPlacementFromUnderUtilizedHost(
vmsToMigrateFromUnderUtilizedHost,
excludedHostsForFindingNewVmPlacement);
excludedHostsForFindingUnderUtilizedHost.addAll(extractHostListFromMigrationMap(newVmPlacement));
migrationMap.addAll(newVmPlacement);
Log.printLine();
}
return migrationMap;
}
/**
* Prints the over utilized hosts.
*
* @param overUtilizedHosts the over utilized hosts
*/
protected void printOverUtilizedHosts(List<PowerHostUtilizationHistory> overUtilizedHosts) {
if (!Log.isDisabled()) {
Log.printLine("Over-utilized hosts:");
for (PowerHostUtilizationHistory host : overUtilizedHosts) {
Log.printConcatLine("Host #", host.getId());
}
Log.printLine();
}
}
/**
* Finds a PM that has enough resources to host a given VM
* and that will not be overloaded after placing the VM on it.
* The selected host will be that one with most efficient
* power usage for the given VM.
*
* @param vm the VM
* @param excludedHosts the excluded hosts
* @return the host found to host the VM
*/
public PowerHost findHostForVm(Vm vm, Set<? extends Host> excludedHosts) {
double minPower = Double.MAX_VALUE;
PowerHost allocatedHost = null;
for (PowerHost host : this.<PowerHost> getHostList()) {
if (excludedHosts.contains(host)) {
continue;
}
if (host.isSuitableForVm(vm)) {
if (getUtilizationOfCpuMips(host) != 0 && isHostOverUtilizedAfterAllocation(host, vm)) {
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;
}
/**
* Checks if a host will be over utilized after placing of a candidate VM.
*
* @param host the host to verify
* @param vm the candidate vm
* @return true, if the host will be over utilized after VM placement; false otherwise
*/
protected boolean isHostOverUtilizedAfterAllocation(PowerHost host, Vm vm) {
boolean isHostOverUtilizedAfterAllocation = true;
if (host.vmCreate(vm)) {
isHostOverUtilizedAfterAllocation = isHostOverUtilized(host);
host.vmDestroy(vm);
}
return isHostOverUtilizedAfterAllocation;
}
@Override
public PowerHost findHostForVm(Vm vm) {
Set<Host> excludedHosts = new HashSet<Host>();
if (vm.getHost() != null) {
excludedHosts.add(vm.getHost());
}
return findHostForVm(vm, excludedHosts);
}
/**
* Extracts the host list from a migration map.
*
* @param migrationMap the migration map
* @return the list
*/
protected List<PowerHost> extractHostListFromMigrationMap(List<Map<String, Object>> migrationMap) {
List<PowerHost> hosts = new LinkedList<PowerHost>();
for (Map<String, Object> map : migrationMap) {
hosts.add((PowerHost) map.get("host"));
}
return hosts;
}
/**
* Gets a new vm placement considering the list of VM to migrate.
*
* @param vmsToMigrate the list of VMs to migrate
* @param excludedHosts the list of hosts that aren't selected as destination hosts
* @return the new vm placement map
*/
protected List<Map<String, Object>> getNewVmPlacement(
List<? extends Vm> vmsToMigrate,
Set<? extends Host> excludedHosts) {
List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
PowerVmList.sortByCpuUtilization(vmsToMigrate);
for (Vm vm : vmsToMigrate) {
PowerHost allocatedHost = findHostForVm(vm, excludedHosts);
if (allocatedHost != null) {
allocatedHost.vmCreate(vm);
Log.printConcatLine("VM #", vm.getId(), " allocated to host #", allocatedHost.getId());
Map<String, Object> migrate = new HashMap<String, Object>();
migrate.put("vm", vm);
migrate.put("host", allocatedHost);
migrationMap.add(migrate);
}
}
return migrationMap;
}
/**
* Gets the new vm placement from under utilized host.
*
* @param vmsToMigrate the list of VMs to migrate
* @param excludedHosts the list of hosts that aren't selected as destination hosts
* @return the new vm placement from under utilized host
*/
protected List<Map<String, Object>> getNewVmPlacementFromUnderUtilizedHost(
List<? extends Vm> vmsToMigrate,
Set<? extends Host> excludedHosts) {
List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
PowerVmList.sortByCpuUtilization(vmsToMigrate);
for (Vm vm : vmsToMigrate) {
PowerHost allocatedHost = findHostForVm(vm, excludedHosts);
if (allocatedHost != null) {
allocatedHost.vmCreate(vm);
Log.printConcatLine("VM #", vm.getId(), " allocated to host #", allocatedHost.getId());
Map<String, Object> migrate = new HashMap<String, Object>();
migrate.put("vm", vm);
migrate.put("host", allocatedHost);
migrationMap.add(migrate);
} else {
Log.printLine("Not all VMs can be reallocated from the host, reallocation cancelled");
for (Map<String, Object> map : migrationMap) {
((Host) map.get("host")).vmDestroy((Vm) map.get("vm"));
}
migrationMap.clear();
break;
}
}
return migrationMap;
}
/**
* Gets the VMs to migrate from hosts.
*
* @param overUtilizedHosts the over utilized hosts
* @return the VMs to migrate from hosts
*/
protected List<? extends Vm>
getVmsToMigrateFromHosts(List<PowerHostUtilizationHistory> overUtilizedHosts) {
List<Vm> vmsToMigrate = new LinkedList<Vm>();
for (PowerHostUtilizationHistory host : overUtilizedHosts) {
while (true) {
Vm vm = getVmSelectionPolicy().getVmToMigrate(host);
if (vm == null) {
break;
}
vmsToMigrate.add(vm);
host.vmDestroy(vm);
if (!isHostOverUtilized(host)) {
break;
}
}
}
return vmsToMigrate;
}
/**
* Gets the VMs to migrate from under utilized host.
*
* @param host the host
* @return the vms to migrate from under utilized host
*/
protected List<? extends Vm> getVmsToMigrateFromUnderUtilizedHost(PowerHost host) {
List<Vm> vmsToMigrate = new LinkedList<Vm>();
for (Vm vm : host.getVmList()) {
if (!vm.isInMigration()) {
vmsToMigrate.add(vm);
}
}
return vmsToMigrate;
}
/**
* Gets the over utilized hosts.
*
* @return the over utilized hosts
*/
protected List<PowerHostUtilizationHistory> getOverUtilizedHosts() {
List<PowerHostUtilizationHistory> overUtilizedHosts = new LinkedList<PowerHostUtilizationHistory>();
for (PowerHostUtilizationHistory host : this.<PowerHostUtilizationHistory> getHostList()) {
if (isHostOverUtilized(host)) {
overUtilizedHosts.add(host);
}
}
return overUtilizedHosts;
}
/**
* Gets the switched off hosts.
*
* @return the switched off hosts
*/
protected List<PowerHost> getSwitchedOffHosts() {
List<PowerHost> switchedOffHosts = new LinkedList<PowerHost>();
for (PowerHost host : this.<PowerHost> getHostList()) {
if (host.getUtilizationOfCpu() == 0) {
switchedOffHosts.add(host);
}
}
return switchedOffHosts;
}
/**
* Gets the most under utilized host.
*
* @param excludedHosts the excluded hosts
* @return the most under utilized host
*/
protected PowerHost getUnderUtilizedHost(Set<? extends Host> excludedHosts) {
double minUtilization = 1;
PowerHost underUtilizedHost = null;
for (PowerHost host : this.<PowerHost> getHostList()) {
if (excludedHosts.contains(host)) {
continue;
}
double utilization = host.getUtilizationOfCpu();
if (utilization > 0 && utilization < minUtilization
&& !areAllVmsMigratingOutOrAnyVmMigratingIn(host)) {
minUtilization = utilization;
underUtilizedHost = host;
}
}
return underUtilizedHost;
}
/**
* Checks whether all VMs of a given host are in migration.
*
* @param host the host
* @return true, if successful
*/
protected boolean areAllVmsMigratingOutOrAnyVmMigratingIn(PowerHost host) {
for (PowerVm vm : host.<PowerVm> getVmList()) {
if (!vm.isInMigration()) {
return false;
}
if (host.getVmsMigratingIn().contains(vm)) {
return true;
}
}
return true;
}
/**
* Checks if host is over utilized.
*
* @param host the host
* @return true, if the host is over utilized; false otherwise
*/
protected abstract boolean isHostOverUtilized(PowerHost host);
/**
* Adds an entry for each history map of a host.
*
* @param host the host to add metric history entries
* @param metric the metric to be added to the metric history map
*/
protected void addHistoryEntry(HostDynamicWorkload host, double metric) {
int hostId = host.getId();
if (!getTimeHistory().containsKey(hostId)) {
getTimeHistory().put(hostId, new LinkedList<Double>());
}
if (!getUtilizationHistory().containsKey(hostId)) {
getUtilizationHistory().put(hostId, new LinkedList<Double>());
}
if (!getMetricHistory().containsKey(hostId)) {
getMetricHistory().put(hostId, new LinkedList<Double>());
}
if (!getTimeHistory().get(hostId).contains(CloudSim.clock())) {
getTimeHistory().get(hostId).add(CloudSim.clock());
getUtilizationHistory().get(hostId).add(host.getUtilizationOfCpu());
getMetricHistory().get(hostId).add(metric);
}
}
/**
* Updates the list of maps between a VM and the host where it is place.
* @see #savedAllocation
*/
protected void saveAllocation() {
getSavedAllocation().clear();
for (Host host : getHostList()) {
for (Vm vm : host.getVmList()) {
if (host.getVmsMigratingIn().contains(vm)) {
continue;
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("host", host);
map.put("vm", vm);
getSavedAllocation().add(map);
}
}
}
/**
* Restore VM allocation from the allocation history.
* @see #savedAllocation
*/
protected void restoreAllocation() {
for (Host host : getHostList()) {
host.vmDestroyAll();
host.reallocateMigratingInVms();
}
for (Map<String, Object> map : getSavedAllocation()) {
Vm vm = (Vm) map.get("vm");
PowerHost host = (PowerHost) map.get("host");
if (!host.vmCreate(vm)) {
Log.printConcatLine("Couldn't restore VM #", vm.getId(), " on host #", host.getId());
System.exit(0);
}
getVmTable().put(vm.getUid(), host);
}
}
/**
* Gets the power consumption of a host after placement of a candidate VM.
* The VM is not in fact placed at the host.
*
* @param host the host
* @param vm the candidate vm
*
* @return the power after allocation
*/
protected double getPowerAfterAllocation(PowerHost host, Vm vm) {
double power = 0;
try {
power = host.getPowerModel().getPower(getMaxUtilizationAfterAllocation(host, vm));
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
return power;
}
/**
* Gets the max power consumption of a host after placement of a candidate VM.
* The VM is not in fact placed at the host.
* We assume that load is balanced between PEs. The only
* restriction is: VM's max MIPS < PE's MIPS
*
* @param host the host
* @param vm the vm
*
* @return the power after allocation
*/
protected double getMaxUtilizationAfterAllocation(PowerHost host, Vm vm) {
double requestedTotalMips = vm.getCurrentRequestedTotalMips();
double hostUtilizationMips = getUtilizationOfCpuMips(host);
double hostPotentialUtilizationMips = hostUtilizationMips + requestedTotalMips;
double pePotentialUtilization = hostPotentialUtilizationMips / host.getTotalMips();
return pePotentialUtilization;
}
/**
* Gets the utilization of the CPU in MIPS for the current potentially allocated VMs.
*
* @param host the host
*
* @return the utilization of the CPU in MIPS
*/
protected double getUtilizationOfCpuMips(PowerHost host) {
double hostUtilizationMips = 0;
for (Vm vm2 : host.getVmList()) {
if (host.getVmsMigratingIn().contains(vm2)) {
// calculate additional potential CPU usage of a migrating in VM
hostUtilizationMips += host.getTotalAllocatedMipsForVm(vm2) * 0.9 / 0.1;
}
hostUtilizationMips += host.getTotalAllocatedMipsForVm(vm2);
}
return hostUtilizationMips;
}
/**
* Gets the saved allocation.
*
* @return the saved allocation
*/
protected List<Map<String, Object>> getSavedAllocation() {
return savedAllocation;
}
/**
* Sets the vm selection policy.
*
* @param vmSelectionPolicy the new vm selection policy
*/
protected void setVmSelectionPolicy(PowerVmSelectionPolicy vmSelectionPolicy) {
this.vmSelectionPolicy = vmSelectionPolicy;
}
/**
* Gets the vm selection policy.
*
* @return the vm selection policy
*/
protected PowerVmSelectionPolicy getVmSelectionPolicy() {
return vmSelectionPolicy;
}
/**
* Gets the utilization history.
*
* @return the utilization history
*/
public Map<Integer, List<Double>> getUtilizationHistory() {
return utilizationHistory;
}
/**
* Gets the metric history.
*
* @return the metric history
*/
public Map<Integer, List<Double>> getMetricHistory() {
return metricHistory;
}
/**
* Gets the time history.
*
* @return the time history
*/
public Map<Integer, List<Double>> getTimeHistory() {
return timeHistory;
}
/**
* Gets the execution time history vm selection.
*
* @return the execution time history vm selection
*/
public List<Double> getExecutionTimeHistoryVmSelection() {
return executionTimeHistoryVmSelection;
}
/**
* Gets the execution time history host selection.
*
* @return the execution time history host selection
*/
public List<Double> getExecutionTimeHistoryHostSelection() {
return executionTimeHistoryHostSelection;
}
/**
* Gets the execution time history vm reallocation.
*
* @return the execution time history vm reallocation
*/
public List<Double> getExecutionTimeHistoryVmReallocation() {
return executionTimeHistoryVmReallocation;
}
/**
* Gets the execution time history total.
*
* @return the execution time history total
*/
public List<Double> getExecutionTimeHistoryTotal() {
return executionTimeHistoryTotal;
}
}