Java tutorial
/* * ======================================================================== * DIScrete event baSed Energy Consumption simulaTor * for Clouds and Federations (DISSECT-CF) * ======================================================================== * * This file is part of DISSECT-CF. * * DISSECT-CF is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * DISSECT-CF 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 Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with DISSECT-CF. If not, see <http://www.gnu.org/licenses/>. * * (C) Copyright 2014, Gabor Kecskemeti (gkecskem@dps.uibk.ac.at, * kecskemeti.gabor@sztaki.mta.hu) */ package hu.mta.sztaki.lpds.cloud.simulator.iaas; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang3.tuple.Pair; import gnu.trove.list.linked.TDoubleLinkedList; import hu.mta.sztaki.lpds.cloud.simulator.DeferredEvent; import hu.mta.sztaki.lpds.cloud.simulator.Timed; import hu.mta.sztaki.lpds.cloud.simulator.energy.powermodelling.PowerState; import hu.mta.sztaki.lpds.cloud.simulator.iaas.constraints.AlterableResourceConstraints; import hu.mta.sztaki.lpds.cloud.simulator.iaas.constraints.ConstantConstraints; import hu.mta.sztaki.lpds.cloud.simulator.iaas.constraints.ResourceConstraints; import hu.mta.sztaki.lpds.cloud.simulator.iaas.constraints.UnalterableConstraintsPropagator; import hu.mta.sztaki.lpds.cloud.simulator.iaas.resourcemodel.ConsumptionEventAdapter; import hu.mta.sztaki.lpds.cloud.simulator.iaas.resourcemodel.MaxMinConsumer; import hu.mta.sztaki.lpds.cloud.simulator.iaas.resourcemodel.MaxMinProvider; import hu.mta.sztaki.lpds.cloud.simulator.iaas.resourcemodel.ResourceConsumption; import hu.mta.sztaki.lpds.cloud.simulator.iaas.resourcemodel.ResourceSpreader; import hu.mta.sztaki.lpds.cloud.simulator.io.NetworkNode; import hu.mta.sztaki.lpds.cloud.simulator.io.NetworkNode.NetworkException; import hu.mta.sztaki.lpds.cloud.simulator.io.Repository; import hu.mta.sztaki.lpds.cloud.simulator.io.StorageObject; import hu.mta.sztaki.lpds.cloud.simulator.io.VirtualAppliance; import hu.mta.sztaki.lpds.cloud.simulator.notifications.SingleNotificationHandler; import hu.mta.sztaki.lpds.cloud.simulator.notifications.StateDependentEventHandler; /** * This class represents a single Physical machine with computing resources as * well as local disks and network connections. * * The PM is a central part of the infrastructure simulation, it allows VM * management, direct access to its resources and also provides several power * management operations (like switch off/on). * * @author "Gabor Kecskemeti, Distributed and Parallel Systems Group, University of Innsbruck (c) 2013" * "Gabor Kecskemeti, Laboratory of Parallel and Distributed Systems, MTA SZTAKI (c) 2012" */ public class PhysicalMachine extends MaxMinProvider implements VMManager<PhysicalMachine, ResourceConstraints> { /** * This is the default length for how long a resource allocation will be * kept before it becomes invalid. * * It is specified in ticks. */ public static final int defaultAllocLen = 1000; /** * This is the recommended length for how long a resource allocation will be * kept before it becomes invalid in case the allocation is made for a VM * migration. * * It is specified in ticks. */ public static final int migrationAllocLen = 1000000; /** * Amount of processing to be done if the PM need to be underutilized. This * constant is used for calculating the emulation of on and off operations * if they are specified with single delays in the PM's constructor. */ public static final double smallUtilization = 0.001; /** * Represents the possible states of the physical machines modeled in the * system * * @author "Gabor Kecskemeti, Distributed and Parallel Systems Group, University of Innsbruck (c) 2013" * */ public static enum State { /** * The machine is completely switched off, minimal consumption is * recorded. */ OFF, /** * The machine is under preparation to serve VMs. Some * consumption is recorded already. */ SWITCHINGON, /** * The machine is currently serving VMs. The machine and * its VMs are consuming energy. */ RUNNING, /** * The machine is about to be switched off. It no longer * accepts VM requests but it still consumes energy. */ SWITCHINGOFF }; /** * These are the PM states which are either leading to running or already * running. */ public static final EnumSet<State> ToOnorRunning = EnumSet.of(State.SWITCHINGON, State.RUNNING); /** * These are the PM states which are either leading to off or already off. */ public static final EnumSet<State> ToOfforOff = EnumSet.of(State.SWITCHINGOFF, State.OFF); /** * This is the list of PM states that mostly consume energy */ public static final EnumSet<State> StatesOfHighEnergyConsumption = EnumSet.of(State.SWITCHINGON, State.RUNNING, State.SWITCHINGOFF); /** * Defines the minimal interface for listeners on PM state changes. * * @author "Gabor Kecskemeti, Laboratory of Parallel and Distributed Systems, MTA SZTAKI (c) 2012" * */ public interface StateChangeListener { /** * This function is called by the PM on subscribed objects when a PM's * state changes. To simplify the implementation of the receiver * objects, this state changed function actually propagates all state * infromation (pre and post) as well as the physical machine's * reference which just went through the state change. * * @param pm * the physical machine that is involved in the state change * @param oldState * the state the PM was in before this state change event was * delivered * @param newState * the new state the PM will be in after the event is * complete */ void stateChanged(PhysicalMachine pm, State oldState, State newState); } /** * This class is strongly connected with the physical machine's class, the * two are collaborating to allow a fluid and minimal hassle operation of * allocating resources on a PM. This allocation is then later on expected * to be used by virtual machines to use. * * Before resource allocations are used they are allocated for a fixed * amount of time. Thus if a user forgets about the allocation, the PMs * resources are not occupied forever. * * @author "Gabor Kecskemeti, Distributed and Parallel Systems Group, University of Innsbruck (c) 2013" * "Gabor Kecskemeti, Laboratory of Parallel and Distributed Systems, MTA SZTAKI (c) 2012" */ public class ResourceAllocation extends DeferredEvent implements VirtualMachine.StateChange { /** * The resource set that is virtually offered to the VM that uses this * allocation. */ public final ResourceConstraints allocated; /** * The resource set that is actually reserved on the PM. If * allocated>realAllocated, then the VM might get resources up to * allocated but only when the PM is not over-committed. */ private final ResourceConstraints realAllocated; /** * The VM that utilizes the allocation in question */ private VirtualMachine user = null; /** * The index of this allocation in the allocation list located in the * PM. Allows rapid cleaning of the allocation list. */ private final int myPromisedIndex; /** * If the resource allocation is not used before its expiry time then * this is going to be true! */ private boolean swept = false; /** * The constructor of the resource allocation to be used by the physical * machine only! * * @param realAlloc * defines the actually reserved resources with the * allocation * @param alloc * defines the promised resources for the user * @param until * defines the duration in ticks for which the allocation * will be kept for the user. This period is useful to allow * advanced scheduling operations where some resources are * occupied in advance in order to ensure rapid VM creation. */ private ResourceAllocation(final ResourceConstraints realAlloc, final ResourceConstraints alloc, final int until) { super(until); allocated = alloc; realAllocated = realAlloc; int prLen = promisedResources.length; int i = 0; for (i = 0; i < prLen && promisedResources[i] != null; i++) ; if (i == prLen) { ResourceAllocation[] alls = new ResourceAllocation[prLen * 2]; System.arraycopy(promisedResources, 0, alls, 0, prLen); promisedResources = alls; promisedResources[prLen] = this; myPromisedIndex = prLen; } else { promisedResources[i] = this; myPromisedIndex = i; } promisedCapacities.singleAdd(realAllocated); internalReallyFreeCaps.subtract(realAllocated); promisedAllocationsCount++; } /** * This function is called by deferred event when the allocation is * expired. The allocation expiry event is only happening if there are * no VMs that use the allocation by the time the expiry period is over. * * <i>REMARK:</i> As expiry is considered a problem in most systems, * this is actually printing out to standard error - as the error is not * critical and there would be no way to propagate the error to the * entity who issued the allocation. */ @Override protected void eventAction() { System.err.println("Warning! Expiring resource allocation."); swept = true; promisedCapacityUpdater(); } /** * updates the PM's respective fields about resource availability */ private void promisedCapacityUpdater() { promisedResources[myPromisedIndex] = null; promisedAllocationsCount--; if (promisedAllocationsCount == 0) { promisedCapacities.subtract(promisedCapacities); } else { promisedCapacities.subtract(realAllocated); } if (isUnUsed()) { internalReallyFreeCaps.singleAdd(realAllocated); } } /** * if a resource allocation is no longer needed (e.g. because another * allocation was used for the same VM), then this way one can not only * cancel the allocation but also ensure that the expiry event is not * fired. */ public void cancel() { super.cancel(); promisedCapacityUpdater(); } /** * To complete the allocation process the VM must be told to use a * particular allocation (e.g. in its migration or starup calls). The VM * then automatically tells this resource allocation object to finalize * the allocation process by calling this use function. * * This function subscribes to the VM's state change events in order to * ensure that VMs that are not running are not really using resources * from the PM. * * <i>WARNING:</i> this function is to be used by the VM only as the VM * knows for what operation it needs to use the allocation. * * @param vm * the virtual machine that will use the allocated resources * in the future * @throws VMManagementException * if the allocation passed to the VM is already expired or * if the allocation is already used by some VM. */ void use(final VirtualMachine vm) throws VMManagementException { if (swept) { throw new VMManagementException("Tried to use an already expired allocation"); } if (user == null) { user = vm; internalAvailableCaps.subtract(realAllocated); vms.add(vm); vm.subscribeStateChange(this); cancel(); } else { throw new VMManagementException("Tried to use a resource allocation more than once!"); } } /** * This function is called by the user VM if it is switching states. The * function actually ensures that not running VMs are not having access * to resources. */ @Override public void stateChanged(VirtualMachine vm, VirtualMachine.State oldState, VirtualMachine.State newState) { if (oldState.equals(VirtualMachine.State.RUNNING)) { release(); } } /** * If a VM no longer needs the resources then this function must be * called. As a result, the PM ensures that the relesed resources are * again available to all interested parties. */ void release() { if (user != null) { vms.remove(user); user.unsubscribeStateChange(this); completedVMs++; internalAvailableCaps.singleAdd(realAllocated); internalReallyFreeCaps.singleAdd(realAllocated); increasingFreeCapacityListenerManager.notifyListeners(Collections.singletonList(realAllocated)); user = null; swept = true; } } /** * Allows users to determine if the allocation is already taken by a VM. * * @return * <ul> * <li><i>true</i> if there is a VM that is using the resource * allocation * <li><i>false</i> otherwise * </ul> */ public boolean isUnUsed() { return user == null; } /** * Determines if the allocation is still available to use * * @return * <ul> * <li><i>true</i> if the allocation is still ok to use * <li><i>false</i>otherwise * </ul> */ public boolean isAvailable() { return isUnUsed() && !swept; } /** * Shows basic information about the allocation useful for debugging */ @Override public String toString() { return "RA(Canc:" + swept + " " + allocated + ")"; } /** * Allows to determine the Physical Machine the particular allocation is * bound to. * * @return the physical machine which offers this allocation. */ public PhysicalMachine getHost() { return PhysicalMachine.this; } } /** * This class handles the delays and activites during the power state change * procedures (e.g., switching off/turning on) * * @author "Gabor Kecskemeti, Distributed and Parallel Systems Group, University of Innsbruck (c) 2013" * "Gabor Kecskemeti, Laboratory of Parallel and Distributed Systems, MTA SZTAKI (c) 2014-" */ public class PowerStateDelayer extends ConsumptionEventAdapter { /** * The state that the delayer must switch to after the power state * change has finished its activites. */ private State newState; /** * The list of tasks to do for the particular power state change. These * tasks are expected to represent activities like the boot process. * * IMPORTANT: For performance reasons, the list is handled from its * tailing: * <ul> * <li>The last item is always the amount processing to be done in the * upcoming task of the power change * <li>the penultimate item is always the maximum amount of processing * to be done in a single tick for the above task. * </ul> */ final TDoubleLinkedList tasksDue; /** * when did the particular power state transition start */ public final long transitionStart; /** * What is the actual resource consumption (task) that is executed on * the PM's direct consumer (i.e. the pure machine) */ ResourceConsumption currentConsumption = null; /** * The constructor of the delayer, it allows the specification of the * tasks (that represent the boot/shutdown or similar operations) to be * done before the power state can be achieved. * * @param tasklist * the tasks to execute in a serial order before the the * specific power state can be set. * * IMPORTANT: For performance reasons, the list is handled * from its tailing: * <ul> * <li>The last item is always the amount processing to be * done in the upcoming task of the power change * <li>the penultimate item is always the maximum amount of * processing to be done in a single tick for the above task. * </ul> * @param newPowerState * the power state to be set after completing all the above * tasks. */ public PowerStateDelayer(final double[] tasklist, final State newPowerState) { onOffEvent = this; newState = newPowerState; tasksDue = new TDoubleLinkedList(); tasksDue.add(tasklist); sendTask(); transitionStart = Timed.getFireCount(); } /** * this function picks the next item (i.e. always the very last members * of the list) from the tasklist and executes it, the function also * ensures that the powerstatedelayer object will receive a notification * once the task is completed. * * if there are no further tasks to execute this function even sets the * new power state of the physical machine with the PM's corresponding * function. */ private void sendTask() { // Did we finish all the tasks for the state change? if (tasksDue.size() == 0) { // Mark the completion of the state change onOffEvent = null; setState(newState); return; } // No we did not, lets send some more to our direct consumer final double totalConsumption = tasksDue.removeAt(0); final double limit = tasksDue.removeAt(0); currentConsumption = new ResourceConsumption(totalConsumption, limit, directConsumer, PhysicalMachine.this, this); if (!currentConsumption.registerConsumption()) { throw new IllegalStateException( "PowerStateChange was not successful because resource consumption could not be registered"); } } /** * the notification handler when a task from the tasklist is complete * * this handler actually sends the next task in. */ @Override public void conComplete() { sendTask(); } /** * if we receive a cancelled event we consider it as an unexpected * behavior and throw an exception. When enabling faults in the system * this behavior might need to change. * * @param problematic * the task that did not succeed */ @Override public void conCancelled(ResourceConsumption problematic) { throw new IllegalStateException("Unexpected termination of one of the state changing tasks"); } /** * if there seems to be further operations needed before the particular * power state can be achieved then this function should be called. * * This function is especially useful to model cases when one state * change has already started but then the user opts for another one * before the previous state change can complete * * @param tasklist * the new tasks to be included */ public void addFurtherTasks(final double[] tasklist) { tasksDue.add(tasklist); } /** * if after the list of tasks are completed we need to reach a different * power state than initially planned then this is the function to go * for. * * This is an optional operation, you only need to call it if the PM * started to go for some other state beforehand that you don't like. * * @param newState * the new power state to switch to after all tasks complete */ public void setNewState(State newState) { this.newState = newState; } } /** * the complete resouce set of the pm */ private final ConstantConstraints totalCapacities; /** * the resource set that does not have a VM running on it * * avaiableCapacities = freeCapacities + promisedCapacities */ private final AlterableResourceConstraints internalAvailableCaps; /** * This is the publicly disclosed set of those resources that are not having * a VM running on them. This field is <b>read only</b>! * * This field can automatically update between two checks! If you need * unaltered data please make a copy (e.g., new * ConstantConstraints(pm.freeCapacities)). */ public final UnalterableConstraintsPropagator availableCapacities; /** * the amount of resources currently allocated but that have no VM assigned * to them */ private AlterableResourceConstraints promisedCapacities = AlterableResourceConstraints.getNoResources(); /** * the amount of resources that are not running a VM or not allocated by * some resource allocation */ private final AlterableResourceConstraints internalReallyFreeCaps; /** * This is the publicly disclosed set of those resources that are not even * having an allocation. This field is <b>read only</b>! * * This field can automatically update between two checks! If you need * unaltered data please make a copy (e.g., new * ConstantConstraints(pm.freeCapacities)). */ public final UnalterableConstraintsPropagator freeCapacities; /** * the internal disk of the physical machine. this field is also used to * represent the PM's network connections as the disk is represented with * the repository interface. */ public final Repository localDisk; /** * this is the array of resource allocations which contain all not yet * used/expired resource allocations. * * This array might contain null values in between two resourceallocation * objects. */ private ResourceAllocation[] promisedResources = new ResourceAllocation[2]; /** * the amount of resource allocations in the promisedResources array. */ private int promisedAllocationsCount = 0; /** * The current state of the PM */ private State currentState = null; /** * the tasks to do when turning the PM on. The format of the array is * documented in the powerstatedelayer class. */ private final double[] onTransition; /** * the tasks to do when switching the PM off. The format of the array is * documented in the powerstatedelayer class. */ private final double[] offTransition; /** * around how many ticks the PM is estimated to run the tasks in the * onTransition array. */ private final long onDelayEstimate; /** * around how many ticks the PM is estimated to run the tasks in the * offTransition array. */ private final long offDelayEstimate; /** * When defining powertransitions for the PM one has to label each * transiton's properties with a kind * * @author "Gabor Kecskemeti, Laboratory of Parallel and Distributed Systems, MTA SZTAKI (c) 2014" * */ public static enum PowerStateKind { /** * the powerstate definitions belong to the cpu and memory resources of * the PM */ host, /** * the powerstate definitions belong to the local disk of the PM */ storage, /** * the powerstate definitions belong to the network * interface of the PM */ network }; /** * Mapping between the various PM states and its representative CPU/memory * power behaviors. */ private final EnumMap<State, PowerState> hostPowerBehavior; /** * Mapping between the various PM states and its representative disk power * behaviors. */ private final EnumMap<State, PowerState> storagePowerBehavior; /** * Mapping between the various PM states and its representative network * power behaviors. */ private final EnumMap<State, PowerState> networkPowerBehavior; /** * the manager of the PM's state change notifications. */ private final StateDependentEventHandler<StateChangeListener, Pair<State, State>> stateListenerManager = new StateDependentEventHandler<PhysicalMachine.StateChangeListener, Pair<State, State>>( new SingleNotificationHandler<StateChangeListener, Pair<State, State>>() { @Override public void sendNotification(final StateChangeListener onObject, final Pair<State, State> states) { onObject.stateChanged(PhysicalMachine.this, states.getLeft(), states.getRight()); } }); /** * the set of currently running virtual machines on this PM */ private final HashSet<VirtualMachine> vms = new HashSet<VirtualMachine>(); // current /** * the publicly available, read only set of currently running virtual * machines on this PM */ public final Set<VirtualMachine> publicVms = Collections.unmodifiableSet(vms); /** * the number of VMs that were using resources from this PM at any time of * the PM's existence */ private long completedVMs = 0; // Past /** * The currently operating state change handler - this field actually shows * if a state change is on the way. * * in order to be really consistent, the field is directly managed by the * powerstatedelayer class. */ private PowerStateDelayer onOffEvent = null; /** * this notification handler is used to send out events when some of the * PM's resources are getting available for others to use */ private final StateDependentEventHandler<CapacityChangeEvent<ResourceConstraints>, List<ResourceConstraints>> increasingFreeCapacityListenerManager = new StateDependentEventHandler<VMManager.CapacityChangeEvent<ResourceConstraints>, List<ResourceConstraints>>( new SingleNotificationHandler<CapacityChangeEvent<ResourceConstraints>, List<ResourceConstraints>>() { @Override public void sendNotification(final CapacityChangeEvent<ResourceConstraints> onObject, final List<ResourceConstraints> recentlyFreedUpResources) { onObject.capacityChanged(freeCapacities, recentlyFreedUpResources); } }); /** * This consumer is added to the PM help simulate the pure (VM less) * operations on the PM. E.g., the VMM's operations can be * resourceconsumptions registered between this consumer and the PM. */ public final MaxMinConsumer directConsumer; /** * shows when the PM's direct consumer cannot be used as a consumer for * tasks (in general it is only usable for external users when the PM is * running) */ private boolean directConsumerUsageMoratory = true; /** * Defines a new physical machine, ensures that there are no VMs running so * far * * @param cores * defines the number of CPU cores this machine has under control * @param perCorePocessing * defines the processing capabilities of a single CPU core in * this machine (in instructions/tick) * @param memory * defines the total physical memory this machine has under * control (in bytes) * @param disk * defines the local physical disk & networking this machine has * under control * @param onD * defines the time delay between the machine's switch on and the * first time it can serve VM requests * @param offD * defines the time delay the machine needs to shut down all of * its operations while it does not serve any more VMs * @param powerTransitions * determines the applied power state transitions while the * physical machine state changes. This is the principal way to * alter a PM's energy consumption behavior. */ public PhysicalMachine(double cores, double perCorePocessing, long memory, Repository disk, int onD, int offD, EnumMap<PowerStateKind, EnumMap<State, PowerState>> powerTransitions) { this(cores, perCorePocessing, memory, disk, new double[] { onD * perCorePocessing * smallUtilization, perCorePocessing * smallUtilization }, new double[] { offD * perCorePocessing * smallUtilization, perCorePocessing * smallUtilization }, powerTransitions); } /** * Makes a copy of the given array to the correct target (on/offTransition), * and calculates the estimates for the on/offDelay * * @param on * which taskset needs to be prepared * @param array * the task array - for format see powerstatedelayer * @return the estimated runtime of all tasks in the array */ private long prepareTransitionalTasks(boolean on, double[] array) { final double[] writeHere = on ? onTransition : offTransition; System.arraycopy(array, 0, writeHere, 0, array.length); long odSum = 0; for (int i = 0; i < array.length; i += 2) { odSum += (long) (array[i] / array[i + 1]); } return odSum; } /** * Defines a new physical machine, ensures that there are no VMs running so * far * * @param cores * defines the number of CPU cores this machine has under control * @param perCorePocessing * defines the processing capabilities of a single CPU core in * this machine (in instructions/tick) * @param memory * defines the total physical memory this machine has under * control (in bytes) * @param disk * defines the local physical disk & networking this machine has * under control * @param turnonOperations * defines the tasks to execute before the PM can be turned on - * this can be considered as the simulation of the boot process. * for the complete definition of this array have a look at the * powerstatedelayer class. * @param switchoffOperations * defines the tasks to execute before the PM can be switched off * - this can be considered as the simulation of the shutdown * process. for the complete definition of this array have a look * at the powerstatedelayer class. * @param powerTransitions * determines the applied power state transitions while the * physical machine state changes. This is the principal way to * alter a PM's energy consumption behavior. */ public PhysicalMachine(double cores, double perCorePocessing, long memory, Repository disk, double[] turnonOperations, double[] switchoffOperations, EnumMap<PowerStateKind, EnumMap<State, PowerState>> powerTransitions) { super(cores * perCorePocessing); // Init resources: totalCapacities = new ConstantConstraints(cores, perCorePocessing, memory); internalAvailableCaps = new AlterableResourceConstraints(totalCapacities); availableCapacities = new UnalterableConstraintsPropagator(internalAvailableCaps); internalReallyFreeCaps = new AlterableResourceConstraints(totalCapacities); freeCapacities = new UnalterableConstraintsPropagator(internalReallyFreeCaps); localDisk = disk; hostPowerBehavior = powerTransitions.get(PowerStateKind.host); storagePowerBehavior = powerTransitions.get(PowerStateKind.storage); networkPowerBehavior = powerTransitions.get(PowerStateKind.network); onTransition = new double[turnonOperations.length]; onDelayEstimate = prepareTransitionalTasks(true, turnonOperations); offTransition = new double[switchoffOperations.length]; offDelayEstimate = prepareTransitionalTasks(false, switchoffOperations); if (hostPowerBehavior == null || storagePowerBehavior == null || networkPowerBehavior == null) { throw new IllegalStateException( "Cannot initialize physical machine without a complete power behavior set"); } setState(State.OFF); directConsumer = new MaxMinConsumer(getPerTickProcessingPower()); } /** * Starts the turn off procedure for the physical machine so it no longer * accepts VM requests but it does not consume anymore * * @param migrateHere * the physical machine where the currently hosted VMs of this VM * should go before the actual switch off operation will happen * @return * <ul> * <li><i>true</i> if the switch off procedure has started * <li><i>false</i> if there are still VMs running and migration * target was not specified thus the switch off is not possible * </ul> * @throws NetworkException * if the migration would need to use network connections that * are not set up correctly. * @throws VMManager.VMManagementException * if the migration fails because the VM is not in correct state * or the PM does not have enough storage for the memory/disk * state */ public boolean switchoff(final PhysicalMachine migrateHere) throws VMManagementException, NetworkException { if (migrateHere != null) { final VirtualMachine[] vmarr = vms.toArray(new VirtualMachine[vms.size()]); class MultiMigrate implements VirtualMachine.StateChange { private int counter = 0; @Override public void stateChanged(VirtualMachine vm, VirtualMachine.State oldState, VirtualMachine.State newState) { switch (newState) { case RUNNING: counter++; break; case SUSPENDED_MIG: // There is a problem but we cannot do here anything // right now... default: } if (counter == vmarr.length) { for (int i = 0; i < vmarr.length; i++) { vmarr[i].unsubscribeStateChange(this); } actualSwitchOff(); } } } MultiMigrate mm = new MultiMigrate(); for (int i = 0; i < vmarr.length; i++) { vmarr[i].subscribeStateChange(mm); migrateVM(vmarr[i], migrateHere); } } else { if (vms.size() + promisedAllocationsCount > 0) { return false; } actualSwitchOff(); } return true; } /** * does the actual switchoff, expected to be run only when there are no VMs * running on the PM */ private void actualSwitchOff() { switch (currentState) { case SWITCHINGON: setState(State.SWITCHINGOFF); onOffEvent.addFurtherTasks(offTransition); onOffEvent.setNewState(State.OFF); break; case RUNNING: setState(State.SWITCHINGOFF); new Timed() { @Override public void tick(final long fires) { ResourceSpreader.FreqSyncer syncer = getSyncer(); // Ensures that the switching off activities are only // started once all runtime activities complete for the // directConsumer if (syncer != null && syncer.isSubscribed() && (underProcessing.size() + toBeAdded.size() - toBeRemoved.size() > 0)) { updateFrequency(syncer.getNextEvent() - fires + 1); } else { unsubscribe(); new PowerStateDelayer(offTransition, State.OFF); } } }.tick(Timed.getFireCount()); break; case OFF: case SWITCHINGOFF: // Nothing to do System.err.println("WARNING: an already off PM was tasked to switch off!"); } } /** * Determines if the machine can be used for VM instantiation. * * @return * <ul> * <li>true if the machine is ready to accept VM requests * <li>false otherwise * </ul> */ public boolean isRunning() { return currentState.equals(State.RUNNING); } /** * retrieves the current state of the PM * * @return the current state */ public State getState() { return currentState; } /** * Turns on the physical machine so it allows energy and resource * consumption (i.e. compute tasks) and opens the possibility to receive VM * requests. */ public void turnon() { switch (currentState) { case SWITCHINGOFF: case OFF: setState(State.SWITCHINGON); if (onOffEvent == null) { new PowerStateDelayer(onTransition, State.RUNNING); } else { onOffEvent.addFurtherTasks(onTransition); onOffEvent.setNewState(State.RUNNING); } break; case RUNNING: case SWITCHINGON: // Nothing to do System.err.println("WARNING: an already running PM was tasked to switch on!"); } } /** * Initiates the migration of a VM to another PM. * * @param vm * the VM to be migrated * @param target * the PM to be used by the VM after the migration completes */ @Override public void migrateVM(final VirtualMachine vm, final PhysicalMachine target) throws VMManagementException, NetworkNode.NetworkException { if (vms.contains(vm)) { vm.migrate(target.allocateResources(vm.getResourceAllocation().allocated, true, migrationAllocLen)); } } /** * Not implemented, would allow VMs to receive more resources in the future */ @Override public void reallocateResources(final VirtualMachine vm, final ResourceConstraints newresources) { } /** * Ensures the requested amount of resources are going to be available in * the foreseeable future on this physical machine. * * @param requested * The amount of resources needed by the caller * @param strict * if the PM should not return an allocation if it cannot * completely meet the request. * @param allocationValidityLength * for how long the PM should keep the allocation in place. * Depending on the purpose of the allocation, it is recommended * to use either the defaultAllocLen or the migrationAllocLen * constants of the PM for this field. * @return With a time limited offer on the requested resources. If the * requested resourceconstraints cannot be met by the function then * it returns with the maximum amount of resources it can serve. If * there are no available resources it returns with <i>null</i>! If * the requested resources can be exactly met then the original * requested resourceconstraints object is stored in the returned * resource allocation. If the resourceconstraints only specified a * minimum resource limit, then a new resourceconstraints object is * returned with details of the maximum possible resource * constraints that can fit into the machine. */ public ResourceAllocation allocateResources(final ResourceConstraints requested, final boolean strict, final int allocationValidityLength) throws VMManagementException { if (!currentState.equals(State.RUNNING)) { throw new VMManagementException("The PM is not running and thus cannot offer resources yet"); } // Basic tests for resource availability for the host if (internalReallyFreeCaps.getRequiredCPUs() == 0 || internalReallyFreeCaps.getRequiredMemory() == 0 || requested.getRequiredProcessingPower() > internalReallyFreeCaps.getRequiredProcessingPower()) { return null; } // Allocation type test (i.e. do we allow underprovisioning?) for (int i = 0; i < promisedResources.length; i++) { ResourceAllocation olderAllocation = promisedResources[i]; if (olderAllocation != null) { if (olderAllocation.allocated.isRequiredProcessingIsMinimum() == requested .isRequiredProcessingIsMinimum()) { break; } else { return null; } } } // Promised resources for the virtual machine double vmCPU = requested.getRequiredCPUs(); long vmMem = requested.getRequiredMemory(); final double vmPrPow = requested.isRequiredProcessingIsMinimum() ? totalCapacities.getRequiredProcessingPower() : requested.getRequiredProcessingPower(); // Actually allocated resources (memory is equivalent in both cases) final double allocPrPow = totalCapacities.getRequiredProcessingPower(); final double allocCPU = vmCPU * requested.getRequiredProcessingPower() / allocPrPow; if (0 <= internalReallyFreeCaps.getRequiredCPUs() - allocCPU) { if (0 <= internalReallyFreeCaps.getRequiredMemory() - requested.getRequiredMemory()) { return new ResourceAllocation(new ConstantConstraints(allocCPU, allocPrPow, vmMem), requested.isRequiredProcessingIsMinimum() ? new ConstantConstraints(vmCPU, vmPrPow, true, vmMem) : requested, allocationValidityLength); } else { vmMem = internalReallyFreeCaps.getRequiredMemory(); } } else if (0 <= internalReallyFreeCaps.getRequiredMemory() - requested.getRequiredMemory()) { vmCPU = internalReallyFreeCaps.getRequiredCPUs(); } else { vmCPU = internalReallyFreeCaps.getRequiredCPUs(); vmMem = internalReallyFreeCaps.getRequiredMemory(); } if (strict) { return null; } else { final ResourceConstraints updatedConstraints = new ConstantConstraints(vmCPU, vmPrPow, requested.isRequiredProcessingIsMinimum(), vmMem); return new ResourceAllocation(updatedConstraints, updatedConstraints, allocationValidityLength); } } /** * check if a particular resource allocation is really issued by this pm. * * @param allocation * the untrusted allocation * @return * <ul> * <li><i>true</i> if the PM issued the allocation * <li><i>false</i> otherwise * </ul> */ private boolean checkAllocationsPresence(final ResourceAllocation allocation) { return promisedResources.length > allocation.myPromisedIndex && promisedResources[allocation.myPromisedIndex] == allocation; } /** * Terminate a resource allocation through the PM's interfaces * * @param allocation * the resource allocation to terminate * @return * <ul> * <li><i>true</i> if the allocation was cancelled * <li><i>false</i> otherwise * </ul> */ public boolean cancelAllocation(final ResourceAllocation allocation) { if (checkAllocationsPresence(allocation)) { allocation.cancel(); return true; } return false; } /** * checks if at least in theory the requested resources could be hosted on * the PM (i.e., when there are no other VMs hosted on the PM). * * @param requested * the resource set to be checked for hostability * @return * <ul> * <li><i>true</i> if the such resource request has a chance of * acceptance * <li><i>false</i> otherwise * </ul> */ public boolean isHostableRequest(final ResourceConstraints requested) { return requested.compareTo(totalCapacities) <= 0; } /** * Bounds a VM to a particular PM on a previously agreed allocation * * @param vm * the VM to be deployed on the PM * @param ra * the resource allocation to be uesed for the VM * @param vaSource * the repository that stores the virtual appliance of the VM * @throws VMManagementException * if the VA is not present on the source, or if the allocation * specified is already expired/used, or if the VM cannot be * switched on for some reason * @throws NetworkNode.NetworkException * if the VA is not transferrable from the vaSource to its * destination */ public void deployVM(final VirtualMachine vm, final ResourceAllocation ra, final Repository vaSource) throws VMManagementException, NetworkNode.NetworkException { if (checkAllocationsPresence(ra)) { final VirtualAppliance va = vm.getVa(); final StorageObject foundLocal = localDisk.lookup(va.id); final StorageObject foundRemote = vaSource == null ? null : vaSource.lookup(va.id); if (foundLocal != null || foundRemote != null) { vm.switchOn(ra, vaSource); } else { throw new VMManagementException("No VA available!"); } } else { throw new VMManagementException("Tried to deploy VM with an expired resource allocation"); } } /** * Initiates a VM on this physical machine. If the physical machine cannot * host VMs for some reason an exception is thrown, if the machine cannot * host this particular VA then a null VM is returned. * * @param va * The appliance for the VM to be created. * @param rc * The resource requirements of the VM * @param vaSource * The storage where the VA resides. * @param count * The number of VMs to be created with the above specification * @return The virtual machine(s) that will be instantiated on the PM. Null * if the constraints specify VMs that cannot fit the available * resources of the machine. * * @throws VMManagementException * If the machine is not accepting requests currently.<br> * If the VM startup has failed. */ public VirtualMachine[] requestVM(final VirtualAppliance va, final ResourceConstraints rc, final Repository vaSource, final int count) throws VMManagementException, NetworkException { final VirtualMachine[] vms = new VirtualMachine[count]; final ResourceAllocation[] ras = new ResourceAllocation[count]; boolean canDeployAll = true; for (int i = 0; i < count && canDeployAll; i++) { ras[i] = allocateResources(rc, true, defaultAllocLen); vms[i] = null; canDeployAll &= ras[i] != null && ras[i].allocated == rc; } if (canDeployAll) { for (int i = 0; i < count; i++) { vms[i] = new VirtualMachine(va); deployVM(vms[i], ras[i], vaSource); } } else { for (int i = 0; i < count && ras[i] != null; i++) { ras[i].cancel(); } } return vms; } /** * Requests a few VMs just as before. Scheduling constraints are ignored * currently! As this is too low level to handle them in the current state * of the simulator. */ @Override public VirtualMachine[] requestVM(VirtualAppliance va, ResourceConstraints rc, Repository vaSource, int count, HashMap<String, Object> schedulingConstraints) throws VMManagementException, NetworkException { return requestVM(va, rc, vaSource, count); } /** * Switches off the VM in question if the VM is hosted by this particular * PM. * * @param vm * to be switched off * @param killTasks * if the VM must be switched off without caring about its tasks * then this should be true. if this is false, then the VM can * only be instructed to be switched off if there are no tasks * currently running on it. * @throws NoSuchVMException * if the VM is not hosted on this PM * @throws VMManagementException * if the VM is not in a state to be switched off */ @Override public void terminateVM(final VirtualMachine vm, final boolean killTasks) throws NoSuchVMException, VMManagementException { if (!vms.contains(vm)) { throw new NoSuchVMException("Termination request was received for an unknown VM"); } vm.switchoff(killTasks); } /** * a method to query the currently running VMs (this can also be accessed * through the public field of publicVms). */ @Override public Collection<VirtualMachine> listVMs() { return publicVms; } /** * a method to determine the number of VMs currently hosted by the PM. * * @return the number of VMs on the PM at the current time instance. */ public int numofCurrentVMs() { return vms.size(); } /** * Determines if a resource consumption (i.e., a compute task utilizing the * CPU of the PM) can be registered on the PM for execution. * * Registration is only allowed if (i.e. the return value is true only if): * <ul> * <li>the PM is running * <li>the consumer of the consumption is either a VM or the directconsumer * of the PM * <li>the consumption is coming from the powerstatedelayer. * </ul> */ @Override protected boolean isAcceptableConsumption(final ResourceConsumption con) { final ResourceSpreader consumer = con.getConsumer(); final boolean internalConsumer = (consumer == directConsumer) && (directConsumerUsageMoratory ? (onOffEvent == null ? false : onOffEvent.currentConsumption == con) : true); final boolean runningVirtualMachine; if (internalConsumer) { runningVirtualMachine = false; } else { if (consumer instanceof VirtualMachine) { runningVirtualMachine = vms.contains((VirtualMachine) consumer); } else { runningVirtualMachine = false; } } return internalConsumer || runningVirtualMachine ? super.isAcceptableConsumption(con) : false; } /** * determines if there are any VMs on the PM or not. * * @return * <ul> * <li><i>true</i> if there are some VMs hosted on the PM. * <li><i>false</i> otherwise * </ul> */ public boolean isHostingVMs() { return !vms.isEmpty(); } /** * gets the number of VMs that have already left the PM but that were * running on it once * * @return the number of past VMs related to this PM */ public long getCompletedVMs() { return completedVMs; } /** * Gets an estimate for the duration (in ticks) of the poweron/off operation * in progress. * * @return the estimated complete duration of the power state changing * operation * @throws IllegalStateException * if there is no power state change in progress */ public long getCurrentOnOffDelay() { if (onOffEvent == null) { switch (currentState) { case OFF: return onDelayEstimate; case RUNNING: return offDelayEstimate; default: throw new IllegalStateException("The onOffEvent is null while doing switchon/off"); } } else { long remainingTime = onOffEvent.transitionStart - Timed.getFireCount(); switch (currentState) { case SWITCHINGOFF: remainingTime += offDelayEstimate; break; case SWITCHINGON: remainingTime += onDelayEstimate; break; default: throw new IllegalStateException("The onOffEvent is not null while not in switchon/off mode"); } return remainingTime; } } /** * offers a nice single line format summary of the properties of this PM - * good for debugging and tracing. */ @Override public String toString() { return "Machine(S:" + currentState + " C:" + internalReallyFreeCaps.getRequiredCPUs() + " M:" + internalReallyFreeCaps.getRequiredMemory() + " " + localDisk.toString() + " " + super.toString() + ")"; } /** * manages the subscriptions for state change events * * @param sl * the listener object which expects state change events */ public void subscribeStateChangeEvents(final StateChangeListener sl) { stateListenerManager.subscribeToEvents(sl); } /** * manages the subscriptions for state change events * * @param sl * the listener object that no longer expects state change events */ public void unsubscribeStateChangeEvents(final StateChangeListener sl) { stateListenerManager.unsubscribeFromEvents(sl); } /** * manages the state change operation of the PM. * * Notifies the state change observers. * * With the state change it also handles the power state changes according * to the hostPowerBehavior, networkPowerBehavior and storagePowerBehavior * maps. * * @param newState * the new PM state to be set. */ private void setState(final State newState) { final State pastState = currentState; currentState = newState; directConsumerUsageMoratory = newState != State.RUNNING; stateListenerManager.notifyListeners(Pair.of(pastState, newState)); // Power state management: setCurrentPowerBehavior(hostPowerBehavior.get(newState)); // TODO: if the repository class also implements proper power state // management then this must move there localDisk.diskinbws.setCurrentPowerBehavior(storagePowerBehavior.get(newState)); localDisk.diskoutbws.setCurrentPowerBehavior(storagePowerBehavior.get(newState)); localDisk.inbws.setCurrentPowerBehavior(networkPowerBehavior.get(newState)); localDisk.outbws.setCurrentPowerBehavior(networkPowerBehavior.get(newState)); } /** * collects the total resource capacity of the PM */ public ResourceConstraints getCapacities() { return totalCapacities; } /** * not implemented */ @Override public void subscribeToCapacityChanges(final CapacityChangeEvent<ResourceConstraints> e) { // FIXME: not important yet } /** * not implemented */ @Override public void unsubscribeFromCapacityChanges(final CapacityChangeEvent<ResourceConstraints> e) { // FIXME: not important yet } /** * manages the subscriptions for free capacity events (i.e. those cases when * there are some resources that are either not allocated anymore or when * there is a VM that terminates on the PM) * * @param e * the listener object which expects free capacity events */ public void subscribeToIncreasingFreeapacityChanges(final CapacityChangeEvent<ResourceConstraints> e) { increasingFreeCapacityListenerManager.subscribeToEvents(e); } /** * manages the subscriptions for free capacity events (i.e. those cases when * there are some resources that are either not allocated anymore or when * there is a VM that terminates on the PM) * * @param e * the listener object that no longer expects free capacity * events */ public void unsubscribeFromIncreasingFreeCapacityChanges(final CapacityChangeEvent<ResourceConstraints> e) { increasingFreeCapacityListenerManager.unsubscribeFromEvents(e); } /** * determines if the direct consumer accepts compute tasks to be registered * * @return * <ul> * <li><i>true</i> if new tasks can be set up between the direct * consumer and the PM * <li><i>false</i> otherwise * </ul> */ public boolean isDirectConsumerUsageMoratory() { return directConsumerUsageMoratory; } }