}
_accountMgr.checkAccess(caller, null, true, vmInstance);
//Check if its a scale "up"
ServiceOfferingVO newServiceOffering = _offeringDao.findById(newServiceOfferingId);
if (newServiceOffering.isDynamic()) {
newServiceOffering.setDynamicFlag(true);
validateCustomParameters(newServiceOffering, customParameters);
newServiceOffering = _offeringDao.getcomputeOffering(newServiceOffering, customParameters);
}
// Check that the specified service offering ID is valid
_itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering);
ServiceOffering currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId());
int newCpu = newServiceOffering.getCpu();
int newMemory = newServiceOffering.getRamSize();
int newSpeed = newServiceOffering.getSpeed();
int currentCpu = currentServiceOffering.getCpu();
int currentMemory = currentServiceOffering.getRamSize();
int currentSpeed = currentServiceOffering.getSpeed();
int memoryDiff = newMemory - currentMemory;
int cpuDiff = newCpu*newSpeed - currentCpu*currentSpeed;
// Don't allow to scale when (Any of the new values less than current values) OR (All current and new values are same)
if( (newSpeed < currentSpeed || newMemory < currentMemory || newCpu < currentCpu)
|| ( newSpeed == currentSpeed && newMemory == currentMemory && newCpu == currentCpu)){
throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering(speed="+ newSpeed + ",cpu=" + newCpu + ",memory=," + newMemory + ")" +
" should have at least one value(cpu/ram) greater than old value and no resource value less than older(speed="+ currentSpeed + ",cpu=" + currentCpu + ",memory=," + currentMemory + ")");
}
// Check resource limits
if (newCpu > currentCpu) {
_resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu,
newCpu - currentCpu);
}
if (newMemory > currentMemory) {
_resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory,
newMemory - currentMemory);
}
// Dynamically upgrade the running vms
boolean success = false;
if (vmInstance.getState().equals(State.Running)) {
int retry = _scaleRetry;
ExcludeList excludes = new ExcludeList();
// Check zone wide flag
boolean enableDynamicallyScaleVm = EnableDynamicallyScaleVm.valueIn(vmInstance.getDataCenterId());
if(!enableDynamicallyScaleVm){
throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin");
}
// Check vm flag
if (!vmInstance.isDynamicallyScalable()) {
throw new CloudRuntimeException("Unable to Scale the vm: " + vmInstance.getUuid() + " as vm does not have tools to support dynamic scaling");
}
// Check disable threshold for cluster is not crossed
HostVO host = _hostDao.findById(vmInstance.getHostId());
if(_capacityMgr.checkIfClusterCrossesThreshold(host.getClusterId(), cpuDiff, memoryDiff)){
throw new CloudRuntimeException("Unable to scale vm: " + vmInstance.getUuid() + " due to insufficient resources");
}
while (retry-- != 0) { // It's != so that it can match -1.
try{
boolean existingHostHasCapacity = false;
// Increment CPU and Memory count accordingly.
if (newCpu > currentCpu) {
_resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long (newCpu - currentCpu));
}
if (memoryDiff > 0) {
_resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long (memoryDiff));
}
// #1 Check existing host has capacity
if( !excludes.shouldAvoid(ApiDBUtils.findHostById(vmInstance.getHostId())) ){
existingHostHasCapacity = _capacityMgr.checkIfHostHasCpuCapability(vmInstance.getHostId(), newCpu, newSpeed)
&& _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff,
(memoryDiff) * 1024L * 1024L, false, _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU),
_capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false);
excludes.addHost(vmInstance.getHostId());
}
// #2 migrate the vm if host doesn't have capacity or is in avoid set
if (!existingHostHasCapacity){
_itMgr.findHostAndMigrate(vmInstance.getUuid(), newServiceOfferingId, excludes);
}
// #3 scale the vm now
_itMgr.upgradeVmDb(vmId, newServiceOfferingId);
if (newServiceOffering.isDynamic()) {
//save the custom values to the database.
saveCustomOfferingDetails(vmId, newServiceOffering);
}
vmInstance = _vmInstanceDao.findById(vmId);
_itMgr.reConfigureVm(vmInstance.getUuid(), currentServiceOffering, existingHostHasCapacity);
success = true;
if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) {
removeCustomOfferingDetails(vmId);
}
return success;
}catch(InsufficientCapacityException e ){
s_logger.warn("Received exception while scaling ",e);
} catch (ResourceUnavailableException e) {
s_logger.warn("Received exception while scaling ",e);
} catch (ConcurrentOperationException e) {
s_logger.warn("Received exception while scaling ",e);
} catch (Exception e) {
s_logger.warn("Received exception while scaling ",e);
} finally {
if(!success){
_itMgr.upgradeVmDb(vmId, currentServiceOffering.getId()); // rollback
// Decrement CPU and Memory count accordingly.
if (newCpu > currentCpu) {
_resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long (newCpu - currentCpu));
}
if (newServiceOffering.isDynamic()) {
removeCustomOfferingDetails(vmId);
}
if (memoryDiff > 0) {
_resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long (memoryDiff));
}