org.cloudbus.cloudsim.hosts.HostSimple Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloudsim-plus Show documentation
Show all versions of cloudsim-plus Show documentation
CloudSim Plus: A modern, highly extensible and easier-to-use Java 8 Framework for Modeling and Simulation of Cloud Computing Infrastructures and Services
/*
* 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.hosts;
import org.cloudbus.cloudsim.core.ChangeableId;
import org.cloudbus.cloudsim.core.Machine;
import org.cloudbus.cloudsim.core.Simulation;
import org.cloudbus.cloudsim.datacenters.Datacenter;
import org.cloudbus.cloudsim.datacenters.DatacenterPowerSupply;
import org.cloudbus.cloudsim.datacenters.DatacenterSimple;
import org.cloudbus.cloudsim.power.models.PowerModel;
import org.cloudbus.cloudsim.provisioners.ResourceProvisioner;
import org.cloudbus.cloudsim.provisioners.ResourceProvisionerSimple;
import org.cloudbus.cloudsim.resources.*;
import org.cloudbus.cloudsim.schedulers.vm.VmScheduler;
import org.cloudbus.cloudsim.schedulers.vm.VmSchedulerSpaceShared;
import org.cloudbus.cloudsim.util.Conversion;
import org.cloudbus.cloudsim.util.TimeUtil;
import org.cloudbus.cloudsim.vms.*;
import org.cloudsimplus.listeners.EventListener;
import org.cloudsimplus.listeners.HostUpdatesVmsProcessingEventInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Map.Entry;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.*;
/**
* A Host class that implements the most basic features of a Physical Machine
* (PM) inside a {@link Datacenter}. It executes actions related to management
* of virtual machines (e.g., creation and destruction). A host has a defined
* policy for provisioning memory and bw, as well as an allocation policy for
* PEs to {@link Vm virtual machines}. A host is associated to a Datacenter and
* can host virtual machines.
*
* @author Rodrigo N. Calheiros
* @author Anton Beloglazov
* @since CloudSim Toolkit 1.0
*/
public class HostSimple implements Host {
private static final Logger LOGGER = LoggerFactory.getLogger(HostSimple.class.getSimpleName());
private static long defaultRamCapacity = (long)Conversion.gigaToMega(10);
private static long defaultBwCapacity = 1000;
private static long defaultStorageCapacity = (long)Conversion.gigaToMega(500);
/** @see #getStateHistory() */
private final List stateHistory;
/**@see #getPowerModel() */
private PowerModel powerModel;
/** @see #getId() */
private long id;
/** @see #isFailed() */
private boolean failed;
private boolean active;
private boolean stateHistoryEnabled;
/** @see #getStartTime() */
private double startTime = -1;
/** @see #getShutdownTime() */
private double shutdownTime;
/** @see #getTotalUpTime() */
private double totalUpTime;
/** @see #getLastBusyTime() */
private double lastBusyTime;
/** @see #getIdleShutdownDeadline() */
private double idleShutdownDeadline;
private final Ram ram;
private final Bandwidth bw;
/** @see #getStorage() */
private Storage storage;
/** @see #getRamProvisioner() */
private ResourceProvisioner ramProvisioner;
/** @see #getBwProvisioner() */
private ResourceProvisioner bwProvisioner;
/** @see #getVmScheduler() */
private VmScheduler vmScheduler;
/** @see #getVmList() */
private final List vmList = new ArrayList<>();
/** @see #getPeList() */
private List peList;
/** @see #getVmsMigratingIn() */
private final Set vmsMigratingIn;
/** @see #getVmsMigratingOut() */
private final Set vmsMigratingOut;
/** @see #getDatacenter() */
private Datacenter datacenter;
/** @see Host#removeOnUpdateProcessingListener(EventListener) */
private final Set> onUpdateProcessingListeners;
/** @see #getSimulation() */
private Simulation simulation;
/** @see #getResources() */
private List resources;
private List provisioners;
private final List vmCreatedList;
/**
* The previous amount of MIPS used.
*/
private double previousUtilizationMips;
/** @see #getFreePesNumber() */
private int freePesNumber;
/** @see #getFailedPesNumber() */
private int failedPesNumber;
/**
* Creates and powers on a Host without a pre-defined ID,
* 10GB of RAM, 1000Mbps of Bandwidth and 500GB of Storage.
* It creates a {@link ResourceProvisionerSimple}
* for RAM and Bandwidth. Finally, it sets a {@link VmSchedulerSpaceShared} as default.
* The ID is automatically set when a List of Hosts is attached
* to a {@link Datacenter}.
*
* @param peList the host's {@link Pe} list
*
* @see ChangeableId#setId(long)
* @see #setRamProvisioner(ResourceProvisioner)
* @see #setBwProvisioner(ResourceProvisioner)
* @see #setVmScheduler(VmScheduler)
* @see #setDefaultRamCapacity(long)
* @see #setDefaultBwCapacity(long)
* @see #setDefaultStorageCapacity(long)
*/
public HostSimple(final List peList) {
this(peList, true);
}
/**
* Creates a Host without a pre-defined ID,
* 10GB of RAM, 1000Mbps of Bandwidth and 500GB of Storage
* and enabling the host to be powered on or not.
*
* It creates a {@link ResourceProvisionerSimple}
* for RAM and Bandwidth. Finally, it sets a {@link VmSchedulerSpaceShared} as default.
* The ID is automatically set when a List of Hosts is attached
* to a {@link Datacenter}.
*
* @param peList the host's {@link Pe} list
* @param activate define the Host activation status: true to power on, false to power off
*
* @see ChangeableId#setId(long)
* @see #setRamProvisioner(ResourceProvisioner)
* @see #setBwProvisioner(ResourceProvisioner)
* @see #setVmScheduler(VmScheduler)
* @see #setDefaultRamCapacity(long)
* @see #setDefaultBwCapacity(long)
* @see #setDefaultStorageCapacity(long)
*/
public HostSimple(final List peList, final boolean activate) {
this(defaultRamCapacity, defaultBwCapacity, defaultStorageCapacity, peList, activate);
}
/**
* Creates and powers on a Host with the given parameters and a {@link VmSchedulerSpaceShared} as default.
*
* @param ramProvisioner the ram provisioner with capacity in Megabytes
* @param bwProvisioner the bw provisioner with capacity in Megabits/s
* @param storage the storage capacity in Megabytes
* @param peList the host's PEs list
*
* @see #setVmScheduler(VmScheduler)
*/
public HostSimple(
final ResourceProvisioner ramProvisioner,
final ResourceProvisioner bwProvisioner,
final long storage,
final List peList)
{
this(ramProvisioner.getCapacity(), bwProvisioner.getCapacity(), storage, peList);
setRamProvisioner(ramProvisioner);
setBwProvisioner(bwProvisioner);
setPeList(peList);
}
/**
* Creates and powers on a Host without a pre-defined ID. It uses a {@link ResourceProvisionerSimple}
* for RAM and Bandwidth and also sets a {@link VmSchedulerSpaceShared} as default.
* The ID is automatically set when a List of Hosts is attached
* to a {@link Datacenter}.
*
* @param ram the RAM capacity in Megabytes
* @param bw the Bandwidth (BW) capacity in Megabits/s
* @param storage the storage capacity in Megabytes
* @param peList the host's {@link Pe} list
*
* @see ChangeableId#setId(long)
* @see #setRamProvisioner(ResourceProvisioner)
* @see #setBwProvisioner(ResourceProvisioner)
* @see #setVmScheduler(VmScheduler)
*/
public HostSimple(final long ram, final long bw, final long storage, final List peList) {
this(ram, bw, storage, peList, true);
}
/**
* Creates a Host without a pre-defined ID. It uses a {@link ResourceProvisionerSimple}
* for RAM and Bandwidth and also sets a {@link VmSchedulerSpaceShared} as default.
* The ID is automatically set when a List of Hosts is attached
* to a {@link Datacenter}.
*
* @param ram the RAM capacity in Megabytes
* @param bw the Bandwidth (BW) capacity in Megabits/s
* @param storage the storage capacity in Megabytes
* @param peList the host's {@link Pe} list
* @param activate define the Host activation status: true to power on, false to power off
*
* @see ChangeableId#setId(long)
* @see #setRamProvisioner(ResourceProvisioner)
* @see #setBwProvisioner(ResourceProvisioner)
* @see #setVmScheduler(VmScheduler)
*/
public HostSimple(final long ram, final long bw, final long storage, final List peList, final boolean activate) {
this.setId(-1);
this.setSimulation(Simulation.NULL);
this.setActive(activate);
this.idleShutdownDeadline = DEF_IDLE_SHUTDOWN_DEADLINE;
this.ram = new Ram(ram);
this.bw = new Bandwidth(bw);
this.setStorage(storage);
this.setRamProvisioner(new ResourceProvisionerSimple());
this.setBwProvisioner(new ResourceProvisionerSimple());
this.setVmScheduler(new VmSchedulerSpaceShared());
this.setPeList(peList);
this.setFailed(false);
this.shutdownTime = -1;
this.setDatacenter(Datacenter.NULL);
this.onUpdateProcessingListeners = new HashSet<>();
this.resources = new ArrayList<>();
this.vmCreatedList = new ArrayList<>();
this.provisioners = new ArrayList<>();
this.vmsMigratingIn = new HashSet<>();
this.vmsMigratingOut = new HashSet<>();
this.powerModel = PowerModel.NULL;
this.stateHistory = new LinkedList<>();
}
/**
* Gets the Default RAM capacity (in MB) for creating Hosts.
* This value is used when the RAM capacity is not given in a Host constructor.
*/
public static long getDefaultRamCapacity() {
return defaultRamCapacity;
}
/**
* Sets the Default RAM capacity (in MB) for creating Hosts.
* This value is used when the RAM capacity is not given in a Host constructor.
*/
public static void setDefaultRamCapacity(final long defaultCapacity) {
Machine.validateCapacity(defaultCapacity);
defaultRamCapacity = defaultCapacity;
}
/**
* Gets the Default Bandwidth capacity (in Mbps) for creating Hosts.
* This value is used when the BW capacity is not given in a Host constructor.
*/
public static long getDefaultBwCapacity() {
return defaultBwCapacity;
}
/**
* Sets the Default Bandwidth capacity (in Mbps) for creating Hosts.
* This value is used when the BW capacity is not given in a Host constructor.
*/
public static void setDefaultBwCapacity(final long defaultCapacity) {
Machine.validateCapacity(defaultCapacity);
defaultBwCapacity = defaultCapacity;
}
/**
* Gets the Default Storage capacity (in MB) for creating Hosts.
* This value is used when the Storage capacity is not given in a Host constructor.
*/
public static long getDefaultStorageCapacity() {
return defaultStorageCapacity;
}
/**
* Sets the Default Storage capacity (in MB) for creating Hosts.
* This value is used when the Storage capacity is not given in a Host constructor.
*/
public static void setDefaultStorageCapacity(final long defaultCapacity) {
Machine.validateCapacity(defaultCapacity);
defaultStorageCapacity = defaultCapacity;
}
@Override
public double getTotalMipsCapacity() {
return peList.stream()
.filter(Pe::isWorking)
.mapToDouble(Pe::getCapacity)
.sum();
}
@SuppressWarnings("ForLoopReplaceableByForEach")
@Override
public double updateProcessing(final double currentTime) {
/*The previous utilization mips is just used when there is a DatacenterPowerSupply instance
attached to the datacenter. Since getting the utilization of CPU is an expensive
operation in large scale experiments, if a Datacenter power supply is not set,
the value is not stored.*/
if(((DatacenterSimple)datacenter).getPowerSupply() != DatacenterPowerSupply.NULL) {
setPreviousUtilizationMips(getCpuMipsUtilization());
}
if (!vmList.isEmpty()) {
lastBusyTime = simulation.clock();
} else if(isIdleEnough(idleShutdownDeadline)){
setActive(false);
}
double nextSimulationTime = Double.MAX_VALUE;
/* Uses an indexed for to avoid ConcurrentModificationException,
* e.g., in cases when Vm is destroyed during simulation execution.*/
for (int i = 0; i < vmList.size(); i++) {
final Vm vm = vmList.get(i);
final double nextTime = vm.updateProcessing(currentTime, vmScheduler.getAllocatedMips(vm));
nextSimulationTime = nextTime > 0 ? Math.min(nextTime, nextSimulationTime) : nextSimulationTime;
}
notifyOnUpdateProcessingListeners(nextSimulationTime);
addStateHistory(currentTime);
return nextSimulationTime;
}
private void notifyOnUpdateProcessingListeners(final double nextSimulationTime) {
onUpdateProcessingListeners.forEach(l -> l.update(HostUpdatesVmsProcessingEventInfo.of(l,this, nextSimulationTime)));
}
@Override
public boolean createVm(final Vm vm) {
if(createVmInternal(vm)) {
addVmToCreatedList(vm);
vm.setHost(this);
vm.setCreated(true);
vm.notifyOnHostAllocationListeners();
if(vm.getStartTime() < 0) {
vm.setStartTime(getSimulation().clock());
}
return true;
}
return false;
}
@Override
public boolean createTemporaryVm(final Vm vm) {
return createVmInternal(vm);
}
private boolean createVmInternal(final Vm vm) {
if(vm instanceof VmGroup){
return false;
}
if(!allocateResourcesForVm(vm, false)){
return false;
}
vmList.add(vm);
return true;
}
/**
* Try to allocate all resources that a VM requires (Storage, RAM, BW and MIPS) to be placed at this Host.
*
* @param vm the VM to try allocating resources to
* @param inMigration If the VM is migrating into the Host or it is being just created for the first time
* @return true if the Vm was placed into the host, false if the Host doesn't have enough resources to allocate the Vm
*/
private boolean allocateResourcesForVm(final Vm vm, final boolean inMigration){
if (!storage.isAmountAvailable(vm.getStorage())) {
logAllocationError(vm, inMigration, "MB", this.getStorage(), vm.getStorage());
return false;
}
if (!ramProvisioner.isSuitableForVm(vm, vm.getCurrentRequestedRam())) {
logAllocationError(vm, inMigration, "MB", this.getRam(), vm.getRam());
return false;
}
if (!bwProvisioner.isSuitableForVm(vm, vm.getCurrentRequestedBw())) {
logAllocationError(vm, inMigration, "Mbps", this.getBw(), vm.getBw());
return false;
}
if (!vmScheduler.isSuitableForVm(vm)) {
return false;
}
vm.setInMigration(inMigration);
allocateResourcesForVm(vm);
return true;
}
private void allocateResourcesForVm(Vm vm) {
ramProvisioner.allocateResourceForVm(vm, vm.getCurrentRequestedRam());
bwProvisioner.allocateResourceForVm(vm, vm.getCurrentRequestedBw());
vmScheduler.allocatePesForVm(vm, vm.getCurrentRequestedMips());
storage.allocateResource(vm.getStorage());
}
private void logAllocationError(
final Vm vm, final boolean inMigration, final String resourceUnit,
final Resource pmResource, final Resource vmRequestedResource)
{
final String migration = inMigration ? "VM Migration" : "VM Creation";
final String msg = pmResource.getAvailableResource() > 0 ? "just "+pmResource.getAvailableResource()+" " + resourceUnit : "no amount";
LOGGER.error(
"{}: {}: [{}] Allocation of {} to {} failed due to lack of {}. Required {} but there is {} available.",
simulation.clockStr(), getClass().getSimpleName(), migration, vm, this,
pmResource.getClass().getSimpleName(), vmRequestedResource.getCapacity(), msg);
}
@Override
public void reallocateMigratingInVms() {
for (final Vm vm : getVmsMigratingIn()) {
if (!vmList.contains(vm)) {
vmList.add(vm);
}
allocateResourcesForVm(vm);
}
}
@Override
public boolean isSuitableForVm(final Vm vm) {
return !isFailed() && hasEnoughResources(vm);
}
private boolean hasEnoughResources(final Vm vm) {
/* Since && is a short-circuit operation,
* the more complex method calls are placed last.
* The freePesNumber and peList.size() are used just to improve performance
* and avoid calling the other complex methods
* when all PEs are used. */
return freePesNumber > 0 && peList.size() >= vm.getNumberOfPes() &&
storage.isAmountAvailable(vm.getStorage()) &&
ramProvisioner.isSuitableForVm(vm, vm.getCurrentRequestedRam()) &&
bwProvisioner.isSuitableForVm(vm, vm.getCurrentRequestedBw()) &&
vmScheduler.isSuitableForVm(vm, vm.getCurrentRequestedMips());
}
@Override
public boolean isActive() {
return this.active;
}
@Override
public boolean hasEverStarted() {
return this.startTime > -1;
}
@Override
public final Host setActive(final boolean activate) {
if(isFailed() && activate){
throw new IllegalStateException("The Host is failed and cannot be activated.");
}
showActivationLogBeforeModification(activate);
if(activate && !this.active) {
setStartTime(getSimulation().clock());
} else if(!activate && this.active){
setShutdownTime(getSimulation().clock());
}
this.active = activate;
return this;
}
/**
* Prints information about the (de)activation of the Host,
* before its status is changed
* @param activate the activation value that is being requested to set
* (and will be set after this method call)
* @see #setActive(boolean)
*/
private void showActivationLogBeforeModification(final boolean activate) {
if(simulation == null || !simulation.isRunning() ) {
return;
}
if(activate && !this.active){
LOGGER.info("{}: {} is being powered on.", getSimulation().clockStr(), this);
}
else if(!activate && this.active){
final String reason = isIdleEnough(idleShutdownDeadline) ? " after becoming idle" : "";
LOGGER.info("{}: {} is being powered off{}.", getSimulation().clockStr(), this, reason);
}
}
@Override
public void destroyVm(final Vm vm) {
if(!vm.isCreated()){
return;
}
destroyVmInternal(vm);
vm.notifyOnHostDeallocationListeners(this);
vm.setStopTime(getSimulation().clock());
}
@Override
public void destroyTemporaryVm(final Vm vm) {
destroyVmInternal(vm);
}
private void destroyVmInternal(final Vm vm) {
deallocateResourcesOfVm(requireNonNull(vm));
vmList.remove(vm);
vm.getBroker().getVmExecList().remove(vm);
}
/**
* Deallocate all resources that a VM was using.
*
* @param vm the VM
*/
protected void deallocateResourcesOfVm(final Vm vm) {
vm.setCreated(false);
ramProvisioner.deallocateResourceForVm(vm);
bwProvisioner.deallocateResourceForVm(vm);
vmScheduler.deallocatePesFromVm(vm);
storage.deallocateResource(vm.getStorage());
}
@Override
public void destroyAllVms() {
deallocateResourcesOfAllVms();
for (final Vm vm : vmList) {
vm.setCreated(false);
storage.deallocateResource(vm.getStorage());
}
vmList.clear();
}
/**
* Deallocate all resources that all VMs were using.
*/
protected void deallocateResourcesOfAllVms() {
ramProvisioner.deallocateResourceForAllVms();
bwProvisioner.deallocateResourceForAllVms();
vmScheduler.deallocatePesForAllVms();
}
/**
* {@inheritDoc}
* @return {@inheritDoc}
* @see #getWorkingPesNumber()
* @see #getFreePesNumber()
* @see #getFailedPesNumber()
*/
@Override
public long getNumberOfPes() {
return peList.size();
}
@Override
public int getFreePesNumber() {
return freePesNumber;
}
/**
* Gets the MIPS share of each Pe that is allocated to a given VM.
*
* @param vm the vm
* @return an array containing the amount of MIPS of each pe that is available to the VM
*/
protected List getAllocatedMipsForVm(final Vm vm) {
return vmScheduler.getAllocatedMips(vm);
}
@Override
public double getTotalAllocatedMipsForVm(final Vm vm) {
return vmScheduler.getTotalAllocatedMipsForVm(vm);
}
/**
* Returns the maximum available MIPS among all the PEs of the host.
*
* @return max mips
*/
protected double getMaxAvailableMips() {
return vmScheduler.getMaxAvailableMips();
}
@Override
public double getMips() {
return peList.stream().mapToDouble(Pe::getCapacity).findFirst().orElse(0);
}
@Override
public double getAvailableMips() {
return vmScheduler.getAvailableMips();
}
@Override
public Resource getBw() {
return bwProvisioner.getResource();
}
@Override
public Resource getRam() {
return ramProvisioner.getResource();
}
@Override
public Resource getStorage() {
return storage;
}
@Override
public long getId() {
return id;
}
@Override
public final void setId(long id) {
this.id = id;
}
@Override
public ResourceProvisioner getRamProvisioner() {
return ramProvisioner;
}
@Override
public final Host setRamProvisioner(final ResourceProvisioner ramProvisioner) {
checkSimulationIsRunningAndAttemptedToChangeHost("RAM");
this.ramProvisioner = requireNonNull(ramProvisioner);
this.ramProvisioner.setResource(ram);
return this;
}
private void checkSimulationIsRunningAndAttemptedToChangeHost(final String resourceName) {
if(simulation.isRunning()){
throw new IllegalStateException("It is not allowed to change a Host's "+resourceName+" after the simulation started.");
}
}
@Override
public ResourceProvisioner getBwProvisioner() {
return bwProvisioner;
}
@Override
public final Host setBwProvisioner(final ResourceProvisioner bwProvisioner) {
checkSimulationIsRunningAndAttemptedToChangeHost("BW");
this.bwProvisioner = requireNonNull(bwProvisioner);
this.bwProvisioner.setResource(bw);
return this;
}
@Override
public VmScheduler getVmScheduler() {
return vmScheduler;
}
@Override
public final Host setVmScheduler(final VmScheduler vmScheduler) {
this.vmScheduler = requireNonNull(vmScheduler);
vmScheduler.setHost(this);
return this;
}
@Override
public double getStartTime() {
return startTime;
}
@Override
public void setStartTime(final double startTime) {
if(startTime < 0){
throw new IllegalArgumentException("Host start time cannot be negative");
}
this.startTime = Math.floor(startTime);
//If the Host is being activated or re-activated, the shutdown time is reset
this.shutdownTime = -1;
}
@Override
public double getShutdownTime() {
return shutdownTime;
}
@Override
public void setShutdownTime(final double shutdownTime) {
if(shutdownTime < 0){
throw new IllegalArgumentException("Host shutdown time cannot be negative");
}
this.shutdownTime = Math.floor(shutdownTime);
this.totalUpTime += getUpTime();
}
@Override
public double getUpTime() {
return active ? simulation.clock() - startTime : shutdownTime - startTime;
}
@Override
public double getTotalUpTime() {
return totalUpTime + (active ? getUpTime() : 0);
}
@Override
public double getUpTimeHours() {
return TimeUtil.secondsToHours(getUpTime());
}
@Override
public double getTotalUpTimeHours() {
return TimeUtil.secondsToHours(getTotalUpTime());
}
@Override
public double getIdleShutdownDeadline() {
return idleShutdownDeadline;
}
@Override
public Host setIdleShutdownDeadline(final double deadline) {
this.idleShutdownDeadline = deadline;
return this;
}
@Override
public List getPeList() {
return peList;
}
/**
* Sets the pe list.
*
* @param peList the new pe list
* @return
*/
protected final Host setPeList(final List peList) {
requireNonNull(peList);
checkSimulationIsRunningAndAttemptedToChangeHost("List of PE");
this.peList = peList;
long peId = this.peList.stream().filter(pe -> pe.getId() > 0).mapToLong(Pe::getId).max().orElse(-1);
final List pesWithoutIds = this.peList.stream().filter(pe -> pe.getId() < 0).collect(toList());
for(final Pe pe: pesWithoutIds){
pe.setId(++peId);
}
failedPesNumber = 0;
freePesNumber = peList.size();
return this;
}
@Override
public List getVmList() {
return (List) Collections.unmodifiableList(vmList);
}
@Override
public List getVmCreatedList() {
return (List) Collections.unmodifiableList(vmCreatedList);
}
protected void addVmToList(final Vm vm){
vmList.add(requireNonNull(vm));
}
protected void addVmToCreatedList(final Vm vm){
vmCreatedList.add(requireNonNull(vm));
}
@Override
public boolean isFailed() {
return failed;
}
@Override
public final boolean setFailed(final boolean failed) {
this.failed = failed;
final Pe.Status newStatus = failed ? Pe.Status.FAILED : Pe.Status.FREE;
setPeStatus(peList, newStatus);
/*Just changes the active state when the Host is set to active.
* In other situations, the active status must remain as it was.
* For example, if the host was inactive and now it's set to failed,
* it must remain inactive.*/
if(failed && this.active){
this.active = false;
}
return true;
}
/**
* Sets the status of a given (sub)list of {@link Pe} to a new status.
* @param peList the (sub)list of {@link Pe} to change the status
* @param newStatus the new status
*/
public final void setPeStatus(final List peList, final Pe.Status newStatus){
/*For performance reasons, stores the number of free and failed PEs
instead of iterating over the PE list every time to find out.*/
for (final Pe pe : peList) {
if(pe.getStatus() == newStatus) {
continue;
}
if(newStatus == Pe.Status.FAILED) {
this.failedPesNumber++;
if(pe.getStatus() == Pe.Status.FREE)
this.freePesNumber--;
} else if(newStatus == Pe.Status.FREE) {
this.freePesNumber++;
if(pe.getStatus() == Pe.Status.FAILED)
this.failedPesNumber--;
}
pe.setStatus(newStatus);
}
}
@Override
public Set getVmsMigratingIn() {
return (Set)vmsMigratingIn;
}
@Override
public boolean addMigratingInVm(final Vm vm) {
if (vmsMigratingIn.contains(vm)) {
return false;
}
vmsMigratingIn.add(vm);
if(!allocateResourcesForVm(vm, true)){
vmsMigratingIn.remove(vm);
return false;
}
updateProcessing(simulation.clock());
vm.getHost().updateProcessing(simulation.clock());
return true;
}
@Override
public void removeMigratingInVm(final Vm vm) {
deallocateResourcesOfVm(vm);
vmsMigratingIn.remove(vm);
vmList.remove(vm);
vm.setInMigration(false);
}
@Override
public Set getVmsMigratingOut() {
return Collections.unmodifiableSet(vmsMigratingOut);
}
@Override
public boolean addVmMigratingOut(final Vm vm) {
return this.vmsMigratingOut.add(vm);
}
@Override
public boolean removeVmMigratingOut(final Vm vm) {
return this.vmsMigratingOut.remove(vm);
}
@Override
public Datacenter getDatacenter() {
return datacenter;
}
@Override
public final void setDatacenter(final Datacenter datacenter) {
checkSimulationIsRunningAndAttemptedToChangeHost("Datacenter");
this.datacenter = datacenter;
}
@Override
public String toString() {
final String dc =
Datacenter.NULL.equals(datacenter) ? "" :
String.format("/DC %d", datacenter.getId());
return String.format("Host %d%s", getId(), dc);
}
@Override
public boolean removeOnUpdateProcessingListener(final EventListener listener) {
return onUpdateProcessingListeners.remove(listener);
}
@Override
public Host addOnUpdateProcessingListener(final EventListener listener) {
if(listener.equals(EventListener.NULL)){
return this;
}
this.onUpdateProcessingListeners.add(requireNonNull(listener));
return this;
}
@Override
public long getAvailableStorage() {
return storage.getAvailableResource();
}
@Override
public int getWorkingPesNumber() {
return peList.size() - getFailedPesNumber();
}
@Override
public int getFailedPesNumber() {
return failedPesNumber;
}
private Host setStorage(final long size) {
this.storage = new Storage(size);
return this;
}
@Override
public Simulation getSimulation() {
return this.simulation;
}
@Override
public double getLastBusyTime() {
return lastBusyTime;
}
@Override
public final Host setSimulation(final Simulation simulation) {
this.simulation = simulation;
return this;
}
/**
* Compare this Host with another one based on {@link #getTotalMipsCapacity()}.
*
* @param o the Host to compare to
* @return {@inheritDoc}
*/
@Override
public int compareTo(final Host o) {
return Double.compare(getTotalMipsCapacity(), o.getTotalMipsCapacity());
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final HostSimple that = (HostSimple) o;
if (id != that.id) return false;
return simulation.equals(that.simulation);
}
@Override
public int hashCode() {
int result = Long.hashCode(id);
result = 31 * result + simulation.hashCode();
return result;
}
@Override
public List getResources() {
if(simulation.isRunning() && resources.isEmpty()){
resources = Arrays.asList(ramProvisioner.getResource(), bwProvisioner.getResource());
}
return Collections.unmodifiableList(resources);
}
@Override
public ResourceProvisioner getProvisioner(final Class resourceClass) {
if(simulation.isRunning() && provisioners.isEmpty()){
provisioners = Arrays.asList(ramProvisioner, bwProvisioner);
}
return provisioners
.stream()
.filter(provisioner -> provisioner.getResource().isSubClassOf(resourceClass))
.findFirst()
.orElse(ResourceProvisioner.NULL);
}
@Override
public List getWorkingPeList() {
return getFilteredPeList(Pe::isWorking);
}
@Override
public List getBusyPeList() {
return getFilteredPeList(Pe::isBusy);
}
@Override
public List getFreePeList() {
return getFilteredPeList(Pe::isFree);
}
private List getFilteredPeList(final Predicate status) {
return peList.stream().filter(status).collect(toList());
}
@Override
public double getCpuPercentUtilization() {
return computeCpuUtilizationPercent(getCpuMipsUtilization());
}
private double computeCpuUtilizationPercent(final double mipsUsage){
final double totalMips = getTotalMipsCapacity();
if(totalMips == 0){
return 0;
}
final double utilization = mipsUsage / totalMips;
return (utilization > 1 && utilization < 1.01 ? 1 : utilization);
}
@Override
public double getCpuMipsUtilization() {
return vmList.stream().mapToDouble(Vm::getTotalCpuMipsUtilization).sum();
}
@Override
public long getRamUtilization() {
return ramProvisioner.getTotalAllocatedResource();
}
@Override
public long getBwUtilization() {
return bwProvisioner.getTotalAllocatedResource();
}
@Override
public SortedMap getUtilizationHistory() {
//Gets a Stream containing the utilization entries for every Vm inside the Host
final Stream> utilizationEntriesStream = this.vmCreatedList
.stream()
.map(Vm::getUtilizationHistory)
.map(this::remapUtilizationHistory)
.flatMap(vmUtilization -> vmUtilization.entrySet().stream());
//Groups the CPU utilization entries by the time the values were collected
return utilizationEntriesStream
.collect(
groupingBy(Entry::getKey, TreeMap::new, summarizingDouble(Entry::getValue))
);
}
@Override
public SortedMap getUtilizationHistorySum() {
/*Remaps the value of an entry inside the Utilization History map.*/
final Function, Double> valueMapper = entry -> entry.getValue().getSum();
return getUtilizationHistory()
.entrySet()
.stream()
.collect(toMap(Entry::getKey, valueMapper, this::mergeFunction, TreeMap::new));
}
/**
* Remaps the entire Vm's {@link UtilizationHistory} by updating the CPU utilization value in each entry
* to correspond to the relative percentage of the Host CPU capacity that Vm is using.
* This is required since the {@link UtilizationHistory} contains the VM's CPU utilization
* relative to the VM's capacity.
*
* @param utilizationHistory the VM {@link UtilizationHistory} with the history entries
* @return
*/
private SortedMap remapUtilizationHistory(final UtilizationHistory utilizationHistory) {
return utilizationHistory
.getHistory()
.entrySet()
.stream()
.collect(
toMap(Entry::getKey, vmUtilizationMapper(utilizationHistory), this::mergeFunction, TreeMap::new)
);
}
/**
* A merge {@link BinaryOperator} used to resolve conflicts when remapping the values of the utilization history map
* using the {@link Collectors#toMap(Function, Function, BinaryOperator, Supplier)}.
*
* If there are two values for the same key, the last value is used.
* However, since we are just remapping an existing map, there won't be such a situation.
*
* @param usage1 the 1st CPU utilization value found for a key
* @param usage2 the 2dn CPU utilization value found for the same key
* @return the higher value between the given two ones
*
* @see #getUtilizationHistorySum()
* @see #remapUtilizationHistory(UtilizationHistory)
*/
private double mergeFunction(final double usage1, final double usage2) {
return Math.max(usage1, usage2);
}
/**
* Receives a Vm {@link UtilizationHistory} and returns a {@link Function} that
* requires a map entry from the history (representing a VM's CPU utilization for a given time),
* and returns the percentage of the Host CPU capacity that such a Vm is using at that time.
* This way, the value that represents how much of the VM's CPU is being used
* will be converted to how much that VM is using from the Host's CPU.
*
* @param utilizationHistory the VM {@link UtilizationHistory} with the history entries
* @return
*/
private Function, Double> vmUtilizationMapper(final UtilizationHistory utilizationHistory) {
//The entry key is the time and the value is the percentage of the VM CPU that is being used
return entry -> ((VmSimple)utilizationHistory.getVm()).hostCpuUtilizationInternal(entry.getValue());
}
@Override
public PowerModel getPowerModel() {
return powerModel;
}
@Override
public Host setPowerModel(final PowerModel powerModel) {
requireNonNull(powerModel);
if(powerModel.getHost() != null && powerModel.getHost() != Host.NULL && !powerModel.getHost().equals(this)){
throw new IllegalStateException("The given PowerModel is already assigned to another Host. Each Host must have its own PowerModel instance.");
}
this.powerModel = powerModel;
powerModel.setHost(this);
return this;
}
@Override
public double getPreviousUtilizationOfCpu() {
return computeCpuUtilizationPercent(previousUtilizationMips);
}
@Override
public void enableStateHistory() {
this.stateHistoryEnabled = true;
}
@Override
public void disableStateHistory() {
this.stateHistoryEnabled = false;
}
@Override
public boolean isStateHistoryEnabled() {
return this.stateHistoryEnabled;
}
/**
* Sets the previous utilization of CPU in mips.
*
* @param previousUtilizationMips the new previous utilization of CPU in
* mips
*/
private void setPreviousUtilizationMips(final double previousUtilizationMips) {
this.previousUtilizationMips = previousUtilizationMips;
}
@Override
public List getFinishedVms() {
return getVmList().stream()
.filter(vm -> !vm.isInMigration())
.filter(vm -> vm.getCurrentRequestedTotalMips() == 0)
.collect(toList());
}
/**
* Adds the VM resource usage to the History if the VM is not migrating into the Host.
* @param vm the VM to add its usage to the history
* @param currentTime the current simulation time
* @return the total allocated MIPS for the given VM
*/
private double addVmResourceUseToHistoryIfNotMigratingIn(final Vm vm, final double currentTime) {
double totalAllocatedMips = getVmScheduler().getTotalAllocatedMipsForVm(vm);
if (getVmsMigratingIn().contains(vm)) {
LOGGER.info("{}: {}: {} is migrating in", getSimulation().clockStr(), this, vm);
return totalAllocatedMips;
}
final double totalRequestedMips = vm.getCurrentRequestedTotalMips();
if (totalAllocatedMips + 0.1 < totalRequestedMips) {
final String reason = getVmsMigratingOut().contains(vm) ? "migration overhead" : "capacity unavailability";
final long notAllocatedMipsByPe = (long)((totalRequestedMips - totalAllocatedMips)/vm.getNumberOfPes());
LOGGER.warn(
"{}: {}: {} MIPS not allocated for each one of the {} PEs from {} due to {}.",
getSimulation().clockStr(), this, notAllocatedMipsByPe, vm.getNumberOfPes(), vm, reason);
}
final VmStateHistoryEntry entry = new VmStateHistoryEntry(
currentTime,
totalAllocatedMips,
totalRequestedMips,
vm.isInMigration() && !getVmsMigratingIn().contains(vm));
vm.addStateHistoryEntry(entry);
if (vm.isInMigration()) {
LOGGER.info("{}: {}: {} is migrating out ", getSimulation().clockStr(), this, vm);
totalAllocatedMips /= getVmScheduler().getMaxCpuUsagePercentDuringOutMigration();
}
return totalAllocatedMips;
}
private void addStateHistory(final double currentTime) {
if(!stateHistoryEnabled){
return;
}
double hostTotalRequestedMips = 0;
for (final Vm vm : getVmList()) {
final double totalRequestedMips = vm.getCurrentRequestedTotalMips();
addVmResourceUseToHistoryIfNotMigratingIn(vm, currentTime);
hostTotalRequestedMips += totalRequestedMips;
}
addStateHistoryEntry(currentTime, getCpuMipsUtilization(), hostTotalRequestedMips, active);
}
/**
* Adds a host state history entry.
*
* @param time the time
* @param allocatedMips the allocated mips
* @param requestedMips the requested mips
* @param isActive the is active
*/
private void addStateHistoryEntry(
final double time,
final double allocatedMips,
final double requestedMips,
final boolean isActive)
{
final HostStateHistoryEntry newState = new HostStateHistoryEntry(time, allocatedMips, requestedMips, isActive);
if (!stateHistory.isEmpty()) {
final HostStateHistoryEntry previousState = stateHistory.get(stateHistory.size() - 1);
if (previousState.getTime() == time) {
stateHistory.set(stateHistory.size() - 1, newState);
return;
}
}
stateHistory.add(newState);
}
@Override
public List getStateHistory() {
return Collections.unmodifiableList(stateHistory);
}
@Override
public List getMigratableVms() {
return vmList.stream()
.filter(vm -> !vm.isInMigration())
.collect(Collectors.toList());
}
}