@Override public String toString() { return "Machine(S:" + currentState + " C:" + internalReallyFreeCaps.getRequiredCPUs() + " M:" + internalReallyFreeCaps.getRequiredMemory() + " " + localDisk.toString() + " " + super.toString() + ")"; }
/** * @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> { public static final int defaultAllocLen = 1000; public static final int migrationAllocLen = 1000000; 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 }; public static final EnumSet<State> ToOnorRunning = EnumSet.of(State.SWITCHINGON, State.RUNNING); public static final EnumSet<State> ToOfforOff = EnumSet.of(State.SWITCHINGOFF, State.OFF); public static final EnumSet<State> StatesOfHighEnergyConsumption = EnumSet.of(State.SWITCHINGON, State.RUNNING, State.SWITCHINGOFF); public interface StateChangeListener { void stateChanged(PhysicalMachine pm, State oldState, State newState); } public class ResourceAllocation extends DeferredEvent implements VirtualMachine.StateChange { public final ResourceConstraints allocated; private final ResourceConstraints realAllocated; private VirtualMachine user = null; private final int myPromisedIndex; private boolean swept = false; 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++; } @Override protected void eventAction() { System.err.println("Warning! Expiring resource allocation."); swept = true; promisedCapacityUpdater(); } private void promisedCapacityUpdater() { promisedResources[myPromisedIndex] = null; promisedAllocationsCount--; if (promisedAllocationsCount == 0) { promisedCapacities.subtract(promisedCapacities); } else { promisedCapacities.subtract(realAllocated); } if (isUnUsed()) { internalReallyFreeCaps.singleAdd(realAllocated); } } public void cancel() { super.cancel(); promisedCapacityUpdater(); } 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!"); } } @Override public void stateChanged( VirtualMachine vm, VirtualMachine.State oldState, VirtualMachine.State newState) { if (oldState.equals(VirtualMachine.State.RUNNING)) { release(); } } 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; } } public boolean isUnUsed() { return user == null; } public boolean isAvailable() { return isUnUsed() && !swept; } @Override public String toString() { return "RA(Canc:" + swept + " " + allocated + ")"; } public PhysicalMachine getHost() { return PhysicalMachine.this; } } public class PowerStateDelayer extends ConsumptionEventAdapter { private State newState; // The end of the list is the upcoming task final TDoubleLinkedList tasksDue; public final long transitionStart; ResourceConsumption currentConsumption = null; public PowerStateDelayer(final double[] tasklist, final State newPowerState) { onOffEvent = this; newState = newPowerState; tasksDue = new TDoubleLinkedList(); tasksDue.add(tasklist); sendTask(); transitionStart = Timed.getFireCount(); } 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"); } } @Override public void conComplete() { sendTask(); } @Override public void conCancelled(ResourceConsumption problematic) { throw new IllegalStateException("Unexpected termination of one of the state changing tasks"); } public void addFurtherTasks(final double[] tasklist) { tasksDue.add(tasklist); } public void setNewState(State newState) { this.newState = newState; } } private final ConstantConstraints totalCapacities; // Available physical resources: private final AlterableResourceConstraints internalAvailableCaps; /** * This field can automatically update between two checks! If you need unaltered data please make * a copy. */ public final UnalterableConstraintsPropagator availableCapacities; private AlterableResourceConstraints promisedCapacities = AlterableResourceConstraints.getNoResources(); private final AlterableResourceConstraints internalReallyFreeCaps; /** * This field can automatically update between two checks! If you need unaltered data please make * a copy. */ public final UnalterableConstraintsPropagator freeCapacities; public final Repository localDisk; private ResourceAllocation[] promisedResources = new ResourceAllocation[2]; private int promisedAllocationsCount = 0; // Internal state management private State currentState = null; private final double[] onTransition; private final double[] offTransition; private final long onDelayEstimate; private final long offDelayEstimate; public static enum PowerStateKind { host, storage, network }; private final EnumMap<State, PowerState> hostPowerBehavior; private final EnumMap<State, PowerState> storagePowerBehavior; private final EnumMap<State, PowerState> networkPowerBehavior; 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()); } }); // Managed VMs private final HashSet<VirtualMachine> vms = new HashSet<VirtualMachine>(); // current public final Set<VirtualMachine> publicVms = Collections.unmodifiableSet(vms); private long completedVMs = 0; // Past // The onOffEvent here is managed by the delayer itself. private PowerStateDelayer onOffEvent = null; 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); } }); // The "hidden" - non VM - consumer (representing the VMM's actions and the // PM's own operations public final MaxMinConsumer directConsumer; 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); } /** * @param on which taskset needs to be prepared * @param array the task array * @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; } 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>true if the switch off procedure has started * <li>false if there are still VMs running and migration target was not specified thus the * switch off is not possible * </ul> * * @throws NetworkException * @throws VMManager.VMManagementException */ 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; } 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); } public State getState() { return currentState; } /** * Turns on the physical machine so it allows energy and resource consumption and opens the * possibility to receive VM requests. */ public void turnon() { switch (currentState) { case SWITCHINGOFF: case OFF: if (onOffEvent == null) { new PowerStateDelayer(onTransition, State.RUNNING); } else { onOffEvent.addFurtherTasks(onTransition); onOffEvent.setNewState(State.RUNNING); } setState(State.SWITCHINGON); break; case RUNNING: case SWITCHINGON: // Nothing to do System.err.println("WARNING: an already running PM was tasked to switch on!"); } } @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)); } } @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 * @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 are available 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); } } private boolean checkAllocationsPresence(final ResourceAllocation allocation) { return promisedResources.length > allocation.myPromisedIndex && promisedResources[allocation.myPromisedIndex] == allocation; } public boolean cancelAllocation(final ResourceAllocation allocation) { if (checkAllocationsPresence(allocation)) { allocation.cancel(); return true; } return false; } public boolean isHostableRequest(final ResourceConstraints requested) { return requested.compareTo(totalCapacities) <= 0; } 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; } /** * 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); } @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); } @Override public Collection<VirtualMachine> listVMs() { return publicVms; } public int numofCurrentVMs() { return vms.size(); } @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; } public boolean isHostingVMs() { return !vms.isEmpty(); } public long getCompletedVMs() { return completedVMs; } 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; } } @Override public String toString() { return "Machine(S:" + currentState + " C:" + internalReallyFreeCaps.getRequiredCPUs() + " M:" + internalReallyFreeCaps.getRequiredMemory() + " " + localDisk.toString() + " " + super.toString() + ")"; } public void subscribeStateChangeEvents(final StateChangeListener sl) { stateListenerManager.subscribeToEvents(sl); } public void unsubscribeStateChangeEvents(final StateChangeListener sl) { stateListenerManager.unsubscribeFromEvents(sl); } 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)); } public ResourceConstraints getCapacities() { return totalCapacities; } @Override public void subscribeToCapacityChanges(final CapacityChangeEvent<ResourceConstraints> e) { // FIXME: not important yet } @Override public void unsubscribeFromCapacityChanges(final CapacityChangeEvent<ResourceConstraints> e) { // FIXME: not important yet } public void subscribeToIncreasingFreeapacityChanges( final CapacityChangeEvent<ResourceConstraints> e) { increasingFreeCapacityListenerManager.subscribeToEvents(e); ; } public void unsubscribeFromIncreasingFreeCapacityChanges( final CapacityChangeEvent<ResourceConstraints> e) { increasingFreeCapacityListenerManager.unsubscribeFromEvents(e); } public boolean isDirectConsumerUsageMoratory() { return directConsumerUsageMoratory; } }
/** * 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 * @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 are available 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); } }