All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cloudbus.cloudsim.datacenters.DatacenterSimple Maven / Gradle / Ivy

Go to download

CloudSim Plus: A modern, highly extensible and easier-to-use Java 8 Framework for Modeling and Simulation of Cloud Computing Infrastructures and Services

There is a newer version: 8.0.0
Show newest version
/*
 * 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.datacenters;

import org.cloudbus.cloudsim.allocationpolicies.VmAllocationPolicy;
import org.cloudbus.cloudsim.cloudlets.Cloudlet;
import org.cloudbus.cloudsim.cloudlets.CloudletExecution;
import org.cloudbus.cloudsim.core.CloudSimEntity;
import org.cloudbus.cloudsim.core.CloudSimTags;
import org.cloudbus.cloudsim.core.Simulation;
import org.cloudbus.cloudsim.core.events.PredicateType;
import org.cloudbus.cloudsim.core.events.SimEvent;
import org.cloudbus.cloudsim.hosts.Host;
import org.cloudbus.cloudsim.network.IcmpPacket;
import org.cloudbus.cloudsim.resources.DatacenterStorage;
import org.cloudbus.cloudsim.resources.FileStorage;
import org.cloudbus.cloudsim.schedulers.cloudlet.CloudletScheduler;
import org.cloudbus.cloudsim.util.Conversion;
import org.cloudbus.cloudsim.vms.Vm;
import org.cloudsimplus.autoscaling.VerticalVmScaling;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static java.util.stream.Collectors.toList;

/**
 * Implements the basic features of a Virtualized Cloud Datacenter. It deals
 * with processing of VM queries (i.e., handling of VMs) instead of processing
 * Cloudlet-related queries.
 *
 * @author Rodrigo N. Calheiros
 * @author Anton Beloglazov
 * @since CloudSim Toolkit 1.0
 */
public class DatacenterSimple extends CloudSimEntity implements Datacenter {
    private static final Logger logger = LoggerFactory.getLogger(DatacenterSimple.class.getSimpleName());

    /**
     * @see #getBandwidthPercentForMigration()
     */
    private double bandwidthPercentForMigration;

    /**
     * Indicates if migrations are disabled or not.
     */
    private boolean migrationsEnabled;

    /**
     * @see #getPower()
     */
    private double power;

    private List hostList;

    /** @see #getCharacteristics() */
    private final DatacenterCharacteristics characteristics;

    /** @see #getVmAllocationPolicy() */
    private VmAllocationPolicy vmAllocationPolicy;

    /** @see #getLastProcessTime() */
    private double lastProcessTime;


    /** @see #getSchedulingInterval() */
    private double schedulingInterval;

    /** @see #getDatacenterStorage() */
	private DatacenterStorage datacenterStorage;

    /**
     * Creates a Datacenter with an empty {@link #getDatacenterStorage() storage}.
     *
     * @param simulation The CloudSim instance that represents the simulation the Entity is related to
     * @param hostList list of {@link Host}s that will compound the Datacenter
     * @param vmAllocationPolicy the policy to be used to allocate VMs into hosts
     */
    public DatacenterSimple(
        final Simulation simulation,
        final List hostList,
        final VmAllocationPolicy vmAllocationPolicy)
    {
        this(simulation, hostList, vmAllocationPolicy, new DatacenterStorage());
    }

    /**
     * Creates a Datacenter attaching a given storage list to its {@link #getDatacenterStorage() storage}.
     *
     * @param simulation The CloudSim instance that represents the simulation the Entity is related to
     * @param hostList list of {@link Host}s that will compound the Datacenter
     * @param vmAllocationPolicy the policy to be used to allocate VMs into hosts
     * @param storageList the storage list to attach to the {@link #getDatacenterStorage() datacenter storage}
     */
    public DatacenterSimple(
        final Simulation simulation,
        final List hostList,
        final VmAllocationPolicy vmAllocationPolicy,
        final List storageList)
    {
        this(simulation, hostList, vmAllocationPolicy, new DatacenterStorage(storageList));
    }

    /**
     * Creates a Datacenter with a given {@link #getDatacenterStorage() storage}.
     *
     * @param simulation The CloudSim instance that represents the simulation the Entity is related to
     * @param hostList list of {@link Host}s that will compound the Datacenter
     * @param vmAllocationPolicy the policy to be used to allocate VMs into hosts
     * @param storage the {@link #getDatacenterStorage() storage} for this Datacenter
     * @see DatacenterStorage#getStorageList()
     */
    public DatacenterSimple(
        final Simulation simulation,
        final List hostList,
        final VmAllocationPolicy vmAllocationPolicy,
        final DatacenterStorage storage)
    {
        super(simulation);
        setHostList(hostList);

        setLastProcessTime(0.0);
        setSchedulingInterval(0);
        setDatacenterStorage(storage);


        this.characteristics = new DatacenterCharacteristicsSimple(this);
        this.bandwidthPercentForMigration = DEF_BANDWIDTH_PERCENT_FOR_MIGRATION;
        migrationsEnabled = true;

        setVmAllocationPolicy(vmAllocationPolicy);
    }

    private void setHostList(final List hostList) {
        Objects.requireNonNull(hostList);
        this.hostList = hostList;
        setupHosts();
    }

    private void setupHosts() {
        for (final Host host : hostList) {
            host.setDatacenter(this);
            host.setSimulation(getSimulation());
        }

        Simulation.setIdForEntitiesWithoutOne(this.hostList);
    }

    @Override
    public void processEvent(final SimEvent ev) {
        processCloudletEvents(ev);
        processVmEvents(ev);
        processNetworkEvents(ev);
    }

    private void processNetworkEvents(final SimEvent ev) {
        switch (ev.getTag()) {
            case CloudSimTags.ICMP_PKT_SUBMIT:
                processPingRequest(ev);
            break;
        }
    }

    /**
     * Process a received event.
     * @param ev the event to be processed
     */
    private void processVmEvents(final SimEvent ev) {
        switch (ev.getTag()) {
            case CloudSimTags.VM_CREATE:
                processVmCreate(ev, false);
            break;
            case CloudSimTags.VM_CREATE_ACK:
                processVmCreate(ev, true);
            break;
            case CloudSimTags.VM_VERTICAL_SCALING:
                requestVmVerticalScaling(ev);
            break;
            case CloudSimTags.VM_DESTROY:
                processVmDestroy(ev, false);
            break;
            case CloudSimTags.VM_DESTROY_ACK:
                processVmDestroy(ev, true);
            break;
            case CloudSimTags.VM_MIGRATE:
                finishVmMigration(ev, false);
            break;
            case CloudSimTags.VM_MIGRATE_ACK:
                finishVmMigration(ev, true);
            break;
            case CloudSimTags.VM_UPDATE_CLOUDLET_PROCESSING_EVENT:
                updateCloudletProcessing();
                checkCloudletsCompletionForAllHosts();
            break;
        }
    }

    /**
     * Process a {@link CloudSimTags#VM_VERTICAL_SCALING} request, trying to scale
     * a Vm resource.
     *
     * @param ev the received  {@link CloudSimTags#VM_VERTICAL_SCALING} event
     * @return true if the Vm was scaled, false otherwise
     */
    private boolean requestVmVerticalScaling(final SimEvent ev) {
        if(!(ev.getData() instanceof VerticalVmScaling)){
            return false;
        }

        return vmAllocationPolicy.scaleVmVertically((VerticalVmScaling)ev.getData());
    }

    private void processCloudletEvents(final SimEvent ev) {
        switch (ev.getTag()) {
            // New Cloudlet arrives
            case CloudSimTags.CLOUDLET_SUBMIT:
                processCloudletSubmit(ev, false);
            break;

            // New Cloudlet arrives, but the sender asks for an ack
            case CloudSimTags.CLOUDLET_SUBMIT_ACK:
                processCloudletSubmit(ev, true);
            break;

            // Cancels a previously submitted Cloudlet
            case CloudSimTags.CLOUDLET_CANCEL:
                processCloudlet(ev, CloudSimTags.CLOUDLET_CANCEL);
            break;

            // Pauses a previously submitted Cloudlet
            case CloudSimTags.CLOUDLET_PAUSE:
                processCloudlet(ev, CloudSimTags.CLOUDLET_PAUSE);
            break;

            // Pauses a previously submitted Cloudlet, but the sender
            // asks for an acknowledgement
            case CloudSimTags.CLOUDLET_PAUSE_ACK:
                processCloudlet(ev, CloudSimTags.CLOUDLET_PAUSE_ACK);
            break;

            // Resumes a previously submitted Cloudlet
            case CloudSimTags.CLOUDLET_RESUME:
                processCloudlet(ev, CloudSimTags.CLOUDLET_RESUME);
            break;

            // Resumes a previously submitted Cloudlet, but the sender
            // asks for an acknowledgement
            case CloudSimTags.CLOUDLET_RESUME_ACK:
                processCloudlet(ev, CloudSimTags.CLOUDLET_RESUME_ACK);
            break;
        }
    }

    /**
     * Processes a ping request.
     *
     * @param ev information about the event just happened
     */
    protected void processPingRequest(final SimEvent ev) {
        final IcmpPacket pkt = (IcmpPacket) ev.getData();
        pkt.setTag(CloudSimTags.ICMP_PKT_RETURN);
        pkt.setDestination(pkt.getSource());

        // returns the packet to the sender
        sendNow(pkt.getSource(), CloudSimTags.ICMP_PKT_RETURN, pkt);
    }

    /**
     * Processes a Cloudlet based on the event type.
     *
     * @param ev information about the event just happened
     * @param type event type
     */
    protected void processCloudlet(final SimEvent ev, final int type) {
        Cloudlet cloudlet;
        try {
            cloudlet = (Cloudlet) ev.getData();
        } catch (ClassCastException e) {
            logger.error("{}: Error in processing Cloudlet: {}", super.getName(), e.getMessage());
            return;
        }

        // begins executing ....
        switch (type) {
            case CloudSimTags.CLOUDLET_CANCEL:
                processCloudletCancel(cloudlet);
                break;
            case CloudSimTags.CLOUDLET_PAUSE:
                processCloudletPause(cloudlet, false);
                break;
            case CloudSimTags.CLOUDLET_PAUSE_ACK:
                processCloudletPause(cloudlet, true);
                break;
            case CloudSimTags.CLOUDLET_RESUME:
                processCloudletResume(cloudlet, false);
                break;
            case CloudSimTags.CLOUDLET_RESUME_ACK:
                processCloudletResume(cloudlet, true);
                break;
            default:
                logger.debug(
                    "{}: Unable to handle a request from {} with event tag = {}",
                    this, ev.getSource().getName(), ev.getTag());

        }
    }
    /**
     * Processes the submission of a Cloudlet by a DatacenterBroker.
     *
     * @param ev information about the event just happened
     * @param ack indicates if the event's sender expects to receive an
     * acknowledge message when the event finishes to be processed
     */
    protected void processCloudletSubmit(final SimEvent ev, final boolean ack) {
        final Cloudlet cl = (Cloudlet) ev.getData();
        if (cl.isFinished()) {
            notifyBrokerAboutFinishedCloudlet(cl, ack);
            return;
        }

        cl.assignToDatacenter(this);
        submitCloudletToVm(cl, ack);
    }

    /**
     * Submits a cloudlet to be executed inside its bind VM.
     *
     * @param cl the cloudlet to the executed
     * @param ack indicates if the Broker is waiting for an ACK after the Datacenter
     * receives the cloudlet submission
     */
    private void submitCloudletToVm(final Cloudlet cl, final boolean ack) {
        // time to transfer cloudlet's files
        final double fileTransferTime = getDatacenterStorage().predictFileTransferTime(cl.getRequiredFiles());

        final CloudletScheduler scheduler = cl.getVm().getCloudletScheduler();
        final double estimatedFinishTime = scheduler.cloudletSubmit(cl, fileTransferTime);

        // if this cloudlet is in the exec queue
        if (estimatedFinishTime > 0.0 && !Double.isInfinite(estimatedFinishTime)) {
            send(this,
                getCloudletProcessingUpdateInterval(estimatedFinishTime),
                CloudSimTags.VM_UPDATE_CLOUDLET_PROCESSING_EVENT);
        }

        sendCloudletSubmitAckToBroker(ack, cl);
    }

    /**
     * Gets the time when the next update of cloudlets has to be performed.
     * This is the minimum value between the {@link #getSchedulingInterval()} and the given time
     * (if the scheduling interval is enable, i.e. if it's greater than 0),
     * which represents when the next update of Cloudlets processing
     * has to be performed.
     *
     * @param nextFinishingCloudletTime the predicted completion time of the earliest finishing cloudlet
     * (which is a relative delay from the current simulation time),
     * or {@link Double#MAX_VALUE} if there is no next Cloudlet to execute
     * @return next time cloudlets processing will be updated
     *
     * @see #updateCloudletProcessing()
     */
    protected double getCloudletProcessingUpdateInterval(final double nextFinishingCloudletTime){
        return (schedulingInterval == 0 ?
            nextFinishingCloudletTime :
            Math.min(nextFinishingCloudletTime, schedulingInterval));
    }

    /**
     * Processes a Cloudlet resume request.
     *
     * @param cloudlet cloudlet to be resumed
     * @param ack indicates if the event's sender expects to receive an
     * acknowledge message when the event finishes to be processed
     */
    protected void processCloudletResume(final Cloudlet cloudlet, final boolean ack) {
        final double estimatedFinishTime = cloudlet.getVm()
            .getCloudletScheduler().cloudletResume(cloudlet.getId());

        if (estimatedFinishTime > 0.0 && estimatedFinishTime > getSimulation().clock()) {
            schedule(this,
                getCloudletProcessingUpdateInterval(estimatedFinishTime),
                CloudSimTags.VM_UPDATE_CLOUDLET_PROCESSING_EVENT);
        }

        if (ack) {
            sendNow(cloudlet.getBroker(), CloudSimTags.CLOUDLET_RESUME_ACK, cloudlet);
        }
    }

    /**
     * Processes a Cloudlet pause request.
     *
     * @param cloudlet cloudlet to be paused
     * @param ack indicates if the event's sender expects to receive an
     * acknowledge message when the event finishes to be processed
     */
    protected void processCloudletPause(final Cloudlet cloudlet, final boolean ack) {
        cloudlet.getVm().getCloudletScheduler().cloudletPause(cloudlet.getId());

        if (ack) {
            sendNow(cloudlet.getBroker(), CloudSimTags.CLOUDLET_PAUSE_ACK, cloudlet);
        }
    }

    /**
     * Processes a Cloudlet cancel request.
     *
     * @param cloudlet cloudlet to be canceled
     */
    protected void processCloudletCancel(final Cloudlet cloudlet) {
        cloudlet.getVm().getCloudletScheduler().cloudletCancel(cloudlet.getId());
        sendNow(cloudlet.getBroker(), CloudSimTags.CLOUDLET_CANCEL, cloudlet);
    }

    /**
     * Process the event for a Broker which wants to create a VM in this
     * Datacenter. This Datacenter will then send the status back to
     * the Broker.
     *
     * @param ev information about the event just happened
     * @param ackRequested indicates if the event's sender expects to receive an
     * acknowledge message when the event finishes to be processed
     * @return true if a host was allocated to the VM; false otherwise
     */
    protected boolean processVmCreate(final SimEvent ev, final boolean ackRequested) {
        final Vm vm = (Vm) ev.getData();

        final boolean hostAllocatedForVm = vmAllocationPolicy.allocateHostForVm(vm);

        if (ackRequested) {
            send(vm.getBroker(), getSimulation().getMinTimeBetweenEvents(), CloudSimTags.VM_CREATE_ACK, vm);
        }

        if (hostAllocatedForVm) {
            if (!vm.isCreated()) {
                vm.setCreated(true);
            }

            final List mipsList = vm.getHost().getVmScheduler().getAllocatedMips(vm);
            vm.updateProcessing(getSimulation().clock(), mipsList);
        }

        return hostAllocatedForVm;
    }

    /**
     * Process the event sent by a Broker, requesting the destruction of a given VM
     * created in this Datacenter. This Datacenter may send, upon
     * request, the status back to the Broker.
     *
     * @param ev information about the event just happened
     * @param ack indicates if the event's sender expects to receive an
     * acknowledge message when the event finishes to be processed
     */
    protected void processVmDestroy(final SimEvent ev, final boolean ack) {
        final Vm vm = (Vm) ev.getData();
        final int cloudlets = vm.getCloudletScheduler().getCloudletList().size();
        vmAllocationPolicy.deallocateHostForVm(vm);

        if (ack) {
            sendNow(vm.getBroker(), CloudSimTags.VM_DESTROY_ACK, vm);
        }

        final String partialMsg = cloudlets == 0 ? "" : String.format("It had a total of %d cloudlets (running + waiting).", cloudlets);
        final String msg = String.format(
                "%.2f: %s: %s destroyed on %s. %s\n",
                getSimulation().clock(), getClass().getSimpleName(), vm, vm.getHost(), partialMsg);
        if(cloudlets == 0)
            logger.info(msg);
        else logger.warn(msg);
    }

    /**
     * Finishes the process of migrating a VM.
     *
     * @param ev information about the event just happened
     * @param ack indicates if the event's sender expects to receive an
     * acknowledge message when the event finishes to be processed
     */
    protected void finishVmMigration(final SimEvent ev, final boolean ack) {
        if (!(ev.getData() instanceof Map.Entry)) {
            throw new ClassCastException("The data object must be Map.Entry");
        }

        final Map.Entry entry = (Map.Entry) ev.getData();

        final Vm vm = entry.getKey();
        final Host targetHost = entry.getValue();

        //Updates processing of all Hosts to get the latest state for all Hosts before migrating VMs
        updateHostsProcessing();

        //Deallocates the VM on the source Host (where it is migrating out)
        vmAllocationPolicy.deallocateHostForVm(vm);

        targetHost.removeMigratingInVm(vm);
        final boolean result = vmAllocationPolicy.allocateHostForVm(vm, targetHost);

        if (ack) {
            sendNow(ev.getSource(), CloudSimTags.VM_CREATE_ACK, vm);
        }

        vm.setInMigration(false);

        final SimEvent event = getSimulation().findFirstDeferred(this, new PredicateType(CloudSimTags.VM_MIGRATE));
        if (event == null || event.eventTime() > getSimulation().clock()) {
            //Updates processing of all Hosts again to get the latest state for all Hosts after the VMs migrations
            updateHostsProcessing();
        }

        if (result)
            logger.info("{}: Migration of {} to {} is completed", getSimulation().clock(), vm, targetHost);
        else logger.error("{}: Allocation of {} to the destination Host failed!", this, vm);
    }

    /**
     * Checks if a submitted cloudlet has already finished.
     * If it is the case, the Datacenter notifies the Broker that
     * the Cloudlet cannot be created again because it has already finished.
     *
     * @param cl the finished cloudlet
     * @param ack indicates if the Broker is waiting for an ACK after the Datacenter
     * receives the cloudlet submission
     */
    private void notifyBrokerAboutFinishedCloudlet(final Cloudlet cl, final boolean ack) {
        logger.warn(
            "{}: {} owned by {} is already completed/finished. It won't be executed again.",
            getName(), cl, cl.getBroker());

        /*
         NOTE: If a Cloudlet has finished, then it won't be processed.
         So, if ack is required, this method sends back a result.
         If ack is not required, this method don't send back a result.
         Hence, this might cause CloudSim to be hanged since waiting
         for this Cloudlet back.
        */
        sendCloudletSubmitAckToBroker(ack, cl);

        sendNow(cl.getBroker(), CloudSimTags.CLOUDLET_RETURN, cl);
    }

    /**
     * Sends an ACK to the DatacenterBroker that submitted the Cloudlet for execution
     * in order to respond the reception of the submission request,
     * informing if the cloudlet was created or not.
     *
     * The ACK is sent just if the Broker is waiting for it and that condition
     * is indicated in the ack parameter.
     *
     * @param ack indicates if the Broker is waiting for an ACK after the Datacenter
     * receives the cloudlet submission
     * @param cl the cloudlet to respond to DatacenterBroker if it was created or not
     */
    private void sendCloudletSubmitAckToBroker(final boolean ack, final Cloudlet cl) {
        if(!ack){
            return;
        }

        sendNow(cl.getBroker(), CloudSimTags.CLOUDLET_SUBMIT_ACK, cl);
    }

    /**
     * Updates the processing of all Hosts, meaning
     * it makes the processing of VMs running inside such hosts to be updated.
     * Finally, the processing of Cloudlets running inside such VMs is updated too.
     *
     * @return the predicted completion time of the earliest finishing cloudlet
     * (which is a relative delay from the current simulation time),
     * or {@link Double#MAX_VALUE} if there is no next Cloudlet to execute
     */
    private double updateHostsProcessing() {
        double nextSimulationTime = Double.MAX_VALUE;
        for (final Host host : getHostList()) {
            final double time = host.updateProcessing(getSimulation().clock());
            nextSimulationTime = Math.min(time, nextSimulationTime);
        }

        // Guarantees a minimal interval before scheduling the event
        final double minTimeBetweenEvents = getSimulation().getMinTimeBetweenEvents()+0.01;
        nextSimulationTime = Math.max(nextSimulationTime, minTimeBetweenEvents);

        if (nextSimulationTime == Double.MAX_VALUE) {
            return nextSimulationTime;
        }

        power += getDatacenterPowerUsageForTimeSpan();

        return nextSimulationTime;
    }

    /**
     * Gets an estimation of total power consumed (in Watts-sec) by all Hosts of the Datacenter
     * since the last time the processing of Cloudlets in this Host was updated.
     *
     * @return the estimated total power consumed (in Watts-sec) by all Hosts in the elapsed time span
     * @see #getPower()
     * @see #getPowerInKWattsHour()
     */
    private double getDatacenterPowerUsageForTimeSpan() {
        if (getSimulation().clock() - getLastProcessTime() == 0) { //time span
            return 0;
        }

        double datacenterTimeSpanPowerUse = 0;
        for (final Host host : this.getHostList()) {
            final double prevCpuUsage = host.getPreviousUtilizationOfCpu();
            final double cpuUsage = host.getUtilizationOfCpu();
            final double timeFrameHostEnergy =
                host.getPowerModel().getEnergyLinearInterpolation(prevCpuUsage, cpuUsage, getSimulation().clock() - getLastProcessTime());
            datacenterTimeSpanPowerUse += timeFrameHostEnergy;
        }

        return datacenterTimeSpanPowerUse;
    }

    /**
     * Updates processing of each Host, that fires the update of VMs,
     * which in turn updates cloudlets running in this Datacenter.
     * After that, the method schedules the next processing update.
     * It is necessary because Hosts and VMs are simple objects, not
     * entities. So, they don't receive events and updating cloudlets inside
     * them must be called from the outside.
     *
     * @return the predicted completion time of the earliest finishing cloudlet
     * (which is a relative delay from the current simulation time),
     * or {@link Double#MAX_VALUE} if there is no next Cloudlet to execute
     * or it isn't time to update the cloudlets
     */
    protected double updateCloudletProcessing() {
        if (!isTimeToUpdateCloudletsProcessing()){
            return Double.MAX_VALUE;
        }

        double nextSimulationTime = updateHostsProcessing();
        if (nextSimulationTime != Double.MAX_VALUE) {
            nextSimulationTime = getCloudletProcessingUpdateInterval(nextSimulationTime);
            schedule(this,
                nextSimulationTime,
                CloudSimTags.VM_UPDATE_CLOUDLET_PROCESSING_EVENT);
        }
        setLastProcessTime(getSimulation().clock());

        checkIfVmMigrationsAreNeeded();
        return nextSimulationTime;
    }

    private boolean isTimeToUpdateCloudletsProcessing() {
        // if some time passed since last processing
        // R: for term is to allow loop at simulation start. Otherwise, one initial
        // simulation step is skipped and schedulers are not properly initialized
        return getSimulation().clock() < 0.111 ||
               getSimulation().clock() >= lastProcessTime + getSimulation().getMinTimeBetweenEvents();
    }

    /**
     * Checks if the {@link #getVmAllocationPolicy()} has defined
     * a new VM placement map, then sends the request to migrate VMs.
     */
    private void checkIfVmMigrationsAreNeeded() {
        if (!isMigrationsEnabled()) {
            return;
        }

        final Map migrationMap = getVmAllocationPolicy().getOptimizedAllocationMap(getVmList());
        for (final Map.Entry entry : migrationMap.entrySet()) {
            requestVmMigration(entry);
        }
    }

    /**
     * Actually fires the event that starts the VM migration
     * @param entry a Map Entry that indicate to which Host a VM must be migrated
     */
    private void requestVmMigration(final Map.Entry entry) {
        final double currentTime = getSimulation().clock();
        final Host sourceHost = entry.getKey().getHost();
        final Host targetHost = entry.getValue();

        final double delay = timeToMigrateVm(entry.getKey(), targetHost);
        String msg1;
        if (sourceHost == Host.NULL) {
            msg1 = String.format(
                "%.2f: Migration of %s to %s is started.",
                currentTime, entry.getKey(), targetHost);
        } else {
            msg1 = String.format(
                "%.2f: Migration of %s from %s to %s is started.",
                currentTime, entry.getKey(), sourceHost, targetHost);
        }

        final String msg2 = String.format(
            "It's expected to finish in %.2f seconds, considering the %.0f%% of bandwidth allowed for migration and the VM RAM size.",
            delay, getBandwidthPercentForMigration()*100);
        logger.info("{}{}{}", msg1, System.lineSeparator(), msg2);


        sourceHost.addVmMigratingOut(entry.getKey());
        targetHost.addMigratingInVm(entry.getKey());

        send(this, delay, CloudSimTags.VM_MIGRATE, entry);
    }

    /**
     * Computes the expected time to migrate a VM to a given Host.
     * It is computed as: VM RAM (MB)/Target Host Bandwidth (Mb/s).
     *
     * @param vm the VM to migrate.
     * @param targetHost the Host where tto migrate the VM
     * @return the time (in seconds) that is expected to migrate the VM
     */
    private double timeToMigrateVm(final Vm vm, final Host targetHost) {
        return vm.getRam().getCapacity() / Conversion.bitesToBytes(targetHost.getBw().getCapacity() * getBandwidthPercentForMigration());
    }

    /**
     * Verifies if some cloudlet inside the hosts of this Datacenter have already finished.
     * If yes, send them to the User/Broker
     */
    protected void checkCloudletsCompletionForAllHosts() {
        final List hosts = vmAllocationPolicy.getHostList();
        hosts.forEach(this::checkCloudletsCompletionForGivenHost);
    }

    private void checkCloudletsCompletionForGivenHost(final Host host) {
        host.getVmList().forEach(this::checkCloudletsCompletionForGivenVm);
    }

    private void checkCloudletsCompletionForGivenVm(final Vm vm) {
        final List nonReturnedCloudlets =
            vm.getCloudletScheduler().getCloudletFinishedList().stream()
                .map(CloudletExecution::getCloudlet)
                .filter(c -> !vm.getCloudletScheduler().isCloudletReturned(c))
                .collect(toList());

        nonReturnedCloudlets.forEach(this::returnFinishedCloudletToBroker);
    }

    /**
     * Notifies the broker about the end of execution of a given Cloudlet,
     * by returning the Cloudlet to it.
     *
     * @param cloudlet the Cloudlet to return to broker in order to notify it about the Cloudlet execution end
     */
    private void returnFinishedCloudletToBroker(final Cloudlet cloudlet) {
        sendNow(cloudlet.getBroker(), CloudSimTags.CLOUDLET_RETURN, cloudlet);
        cloudlet.getVm().getCloudletScheduler().addCloudletToReturnedList(cloudlet);
    }

    @Override
    public void shutdownEntity() {
        super.shutdownEntity();
        logger.info("{}: {} is shutting down...", getSimulation().clock(), getName());
    }

    @Override
    protected void startEntity() {
        logger.info("{} is starting...", getName());
        sendNow(getSimulation().getCloudInfoService(), CloudSimTags.DATACENTER_REGISTRATION_REQUEST, this);
    }

    @Override
    public  List getHostList() {
        return (List)Collections.unmodifiableList(hostList);
    }

    @Override
    public DatacenterCharacteristics getCharacteristics() {
        return characteristics;
    }

    @Override
    public VmAllocationPolicy getVmAllocationPolicy() {
        return vmAllocationPolicy;
    }

    /**
     * Sets the policy to be used by the Datacenter to allocate VMs into hosts.
     *
     * @param vmAllocationPolicy the new vm allocation policy
     */
    public final Datacenter setVmAllocationPolicy(final VmAllocationPolicy vmAllocationPolicy) {
        Objects.requireNonNull(vmAllocationPolicy);
        if(vmAllocationPolicy.getDatacenter() != null && vmAllocationPolicy.getDatacenter() != Datacenter.NULL && !this.equals(vmAllocationPolicy.getDatacenter())){
            throw new IllegalStateException("The given VmAllocationPolicy is already used by another Datacenter.");
        }

        vmAllocationPolicy.setDatacenter(this);
        this.vmAllocationPolicy = vmAllocationPolicy;
        return this;
    }

    /**
     * Gets the last time some cloudlet was processed in the Datacenter.
     *
     * @return the last process time
     */
    protected double getLastProcessTime() {
        return lastProcessTime;
    }

    /**
     * Sets the last time some cloudlet was processed in the Datacenter.
     *
     * @param lastProcessTime the new last process time
     */
    protected final void setLastProcessTime(final double lastProcessTime) {
        this.lastProcessTime = lastProcessTime;
    }

    @Override
    public DatacenterStorage getDatacenterStorage() {
        return this.datacenterStorage;
    }

    @Override
    public final void setDatacenterStorage(final DatacenterStorage datacenterStorage) {
        datacenterStorage.setDatacenter(this);
        this.datacenterStorage = datacenterStorage;
    }

    @Override
    public  List getVmList() {
        return (List) Collections.unmodifiableList(
                getHostList().stream()
                    .flatMap(h -> h.getVmList().stream())
                    .collect(toList()));
    }

    @Override
    public double getSchedulingInterval() {
        return schedulingInterval;
    }

    @Override
    public final Datacenter setSchedulingInterval(final double schedulingInterval) {
        this.schedulingInterval = Math.max(schedulingInterval, 0);
        return this;
    }

    @Override
    public Host getHost(final int index) {
        if (index >= 0 && index < getHostList().size()) {
            return getHostList().get(index);
        }

        return Host.NULL;
    }

    @Override
    public  Datacenter addHostList(final List hostList) {
        hostList.forEach(this::addHost);
        return this;
    }

    @Override
    public  Datacenter addHost(final T host) {
        if(vmAllocationPolicy == null || vmAllocationPolicy == VmAllocationPolicy.NULL){
            throw new IllegalStateException("A VmAllocationPolicy must be set before adding a new Host to the Datacenter.");
        }

        if(host.getId() <= -1) {
            host.setId(getHostList().size());
        }

        host.setDatacenter(this);
        ((List)hostList).add(host);

        //Sets the Datacenter again so that the new Host is registered internally on the VmAllocationPolicy
        vmAllocationPolicy.setDatacenter(this);
        return this;
    }

    @Override
    public String toString() {
        return String.format("Datacenter %d", getId());
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;

        final DatacenterSimple that = (DatacenterSimple) o;

        return !characteristics.equals(that.characteristics);
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + characteristics.hashCode();
        return result;
    }

    @Override
    public double getBandwidthPercentForMigration() {
        return bandwidthPercentForMigration;
    }

    @Override
    public void setBandwidthPercentForMigration(final double bandwidthPercentForMigration) {
        if(bandwidthPercentForMigration <= 0){
            throw new IllegalArgumentException("The bandwidth migration percentage must be greater than 0.");
        }

        if(bandwidthPercentForMigration > 1){
            throw new IllegalArgumentException("The bandwidth migration percentage must be lower or equal to 1.");
        }

        this.bandwidthPercentForMigration = bandwidthPercentForMigration;
    }

    @Override
    public double getPower() {
        return power;
    }

    /**
     * Checks if migrations are enabled.
     *
     * @return true, if migrations are enable; false otherwise
     */
    public boolean isMigrationsEnabled() {
        return migrationsEnabled;
    }

    /**
     * Enable VM migrations.
     *
     * @return
     */
    public final Datacenter enableMigrations() {
        this.migrationsEnabled = true;
        return this;
    }

    /**
     * Disable VM migrations.
     *
     * @return
     */
    public final Datacenter disableMigrations() {
        this.migrationsEnabled = false;
        return this;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy