Skip to content

Commit 4bcd509

Browse files
abh1sarDaan Hoogland
authored andcommitted
Fix resource limit reservation and check during StartVirtualMachine
1 parent 06ee2fe commit 4bcd509

1 file changed

Lines changed: 140 additions & 130 deletions

File tree

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 140 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -5493,13 +5493,135 @@ public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMach
54935493
return startVirtualMachine(vmId, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, true);
54945494
}
54955495

5496+
private Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachineUnchecked(UserVmVO vm, VMTemplateVO template, Long podId,
5497+
Long clusterId, Long hostId, @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse,
5498+
boolean isExplicitHost, boolean isRootAdmin) throws ResourceUnavailableException, InsufficientCapacityException {
5499+
5500+
// check if vm is security group enabled
5501+
if (_securityGroupMgr.isVmSecurityGroupEnabled(vm.getId()) && _securityGroupMgr.getSecurityGroupsForVm(vm.getId()).isEmpty()
5502+
&& !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vm.getId()) && _networkModel.canAddDefaultSecurityGroup()) {
5503+
// if vm is not mapped to security group, create a mapping
5504+
if (logger.isDebugEnabled()) {
5505+
logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically");
5506+
}
5507+
5508+
SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId());
5509+
if (defaultSecurityGroup != null) {
5510+
List<Long> groupList = new ArrayList<>();
5511+
groupList.add(defaultSecurityGroup.getId());
5512+
_securityGroupMgr.addInstanceToGroups(vm, groupList);
5513+
}
5514+
}
5515+
5516+
// Choose deployment planner
5517+
// Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd
5518+
// Default behaviour is invoked when host, cluster or pod are not specified
5519+
Pod destinationPod = getDestinationPod(podId, isRootAdmin);
5520+
Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin);
5521+
HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost);
5522+
DataCenterDeployment plan = null;
5523+
boolean deployOnGivenHost = false;
5524+
if (destinationHost != null) {
5525+
logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM");
5526+
_hostDao.loadHostTags(destinationHost);
5527+
validateStrictHostTagCheck(vm, destinationHost);
5528+
5529+
final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
5530+
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false);
5531+
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
5532+
String errorMsg;
5533+
if (!cpuCapabilityAndCapacity.first()) {
5534+
errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost);
5535+
} else {
5536+
errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost);
5537+
}
5538+
logger.info(errorMsg);
5539+
if (!AllowDeployVmIfGivenHostFails.value()) {
5540+
throw new InvalidParameterValueException(errorMsg);
5541+
}
5542+
} else {
5543+
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null);
5544+
if (!AllowDeployVmIfGivenHostFails.value()) {
5545+
deployOnGivenHost = true;
5546+
}
5547+
}
5548+
} else if (destinationCluster != null) {
5549+
logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM");
5550+
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null);
5551+
if (!AllowDeployVmIfGivenHostFails.value()) {
5552+
deployOnGivenHost = true;
5553+
}
5554+
} else if (destinationPod != null) {
5555+
logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM");
5556+
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null);
5557+
if (!AllowDeployVmIfGivenHostFails.value()) {
5558+
deployOnGivenHost = true;
5559+
}
5560+
}
5561+
5562+
// Set parameters
5563+
Map<VirtualMachineProfile.Param, Object> params = null;
5564+
if (vm.isUpdateParameters()) {
5565+
_vmDao.loadDetails(vm);
5566+
String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template);
5567+
if (!validPassword(password)) {
5568+
throw new InvalidParameterValueException("A valid password for this virtual machine was not provided.");
5569+
}
5570+
// Check if an SSH key pair was selected for the instance and if so
5571+
// use it to encrypt & save the vm password
5572+
encryptAndStorePassword(vm, password);
5573+
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password);
5574+
}
5575+
5576+
if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) {
5577+
if (!HypervisorType.VMware.equals(vm.getHypervisorType())) {
5578+
throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType());
5579+
}
5580+
Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup);
5581+
if (logger.isTraceEnabled()) {
5582+
logger.trace("It was specified whether to enter setup mode: " + paramValue.toString());
5583+
}
5584+
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue);
5585+
}
5586+
5587+
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
5588+
5589+
DeploymentPlanner planner = null;
5590+
if (deploymentPlannerToUse != null) {
5591+
// if set to null, the deployment planner would be later figured out either from global config var, or from
5592+
// the service offering
5593+
planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse);
5594+
if (planner == null) {
5595+
throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse);
5596+
}
5597+
}
5598+
vmEntity.setParamsToEntity(additionalParams);
5599+
5600+
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
5601+
String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId()));
5602+
vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost);
5603+
5604+
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair = new Pair(vm, params);
5605+
if (vm.isUpdateParameters()) {
5606+
// this value is not being sent to the backend; need only for api
5607+
// display purposes
5608+
if (template.isEnablePassword()) {
5609+
if (vm.getDetail(VmDetailConstants.PASSWORD) != null) {
5610+
userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD);
5611+
}
5612+
vm.setUpdateParameters(false);
5613+
_vmDao.update(vm.getId(), vm);
5614+
}
5615+
}
5616+
return vmParamPair;
5617+
}
5618+
54965619
@Override
54975620
public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId,
54985621
@NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse, boolean isExplicitHost)
54995622
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
55005623
// Input validation
55015624
final Account callerAccount = CallContext.current().getCallingAccount();
5502-
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
55035625

55045626
// if account is removed, return error
55055627
if (callerAccount == null || callerAccount.getRemoved() != null) {
@@ -5527,138 +5649,26 @@ public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMach
55275649
if (owner.getState() == Account.State.DISABLED) {
55285650
throw new PermissionDeniedException(String.format("The owner of %s is disabled: %s", vm, owner));
55295651
}
5530-
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair;
5531-
try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, vm.getId(), null, 1L, reservationDao, _resourceLimitMgr)) {
5532-
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
5533-
if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
5534-
// check if account/domain is with in resource limits to start a new vm
5535-
ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
5536-
resourceLimitService.checkVmResourceLimit(owner, vm.isDisplayVm(), offering, template);
5537-
}
5538-
// check if vm is security group enabled
5539-
if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty()
5540-
&& !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) {
5541-
// if vm is not mapped to security group, create a mapping
5542-
if (logger.isDebugEnabled()) {
5543-
logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically");
5544-
}
5545-
5546-
SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId());
5547-
if (defaultSecurityGroup != null) {
5548-
List<Long> groupList = new ArrayList<>();
5549-
groupList.add(defaultSecurityGroup.getId());
5550-
_securityGroupMgr.addInstanceToGroups(vm, groupList);
5551-
}
5552-
}
5553-
// Choose deployment planner
5554-
// Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd
5555-
// Default behaviour is invoked when host, cluster or pod are not specified
5556-
boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId());
5557-
Pod destinationPod = getDestinationPod(podId, isRootAdmin);
5558-
Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin);
5559-
HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost);
5560-
DataCenterDeployment plan = null;
5561-
boolean deployOnGivenHost = false;
5562-
if (destinationHost != null) {
5563-
logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM");
5564-
_hostDao.loadHostTags(destinationHost);
5565-
validateStrictHostTagCheck(vm, destinationHost);
5566-
5567-
final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
5568-
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false);
5569-
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
5570-
String errorMsg;
5571-
if (!cpuCapabilityAndCapacity.first()) {
5572-
errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost);
5573-
} else {
5574-
errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost);
5575-
}
5576-
logger.info(errorMsg);
5577-
if (!AllowDeployVmIfGivenHostFails.value()) {
5578-
throw new InvalidParameterValueException(errorMsg);
5579-
}
5580-
} else {
5581-
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null);
5582-
if (!AllowDeployVmIfGivenHostFails.value()) {
5583-
deployOnGivenHost = true;
5584-
}
5585-
}
5586-
} else if (destinationCluster != null) {
5587-
logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM");
5588-
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null);
5589-
if (!AllowDeployVmIfGivenHostFails.value()) {
5590-
deployOnGivenHost = true;
5591-
}
5592-
} else if (destinationPod != null) {
5593-
logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM");
5594-
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null);
5595-
if (!AllowDeployVmIfGivenHostFails.value()) {
5596-
deployOnGivenHost = true;
5597-
}
5598-
}
5599-
5600-
// Set parameters
5601-
Map<VirtualMachineProfile.Param, Object> params = null;
5602-
if (vm.isUpdateParameters()) {
5603-
_vmDao.loadDetails(vm);
5604-
5605-
String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template);
5606-
5607-
if (!validPassword(password)) {
5608-
throw new InvalidParameterValueException("A valid password for this virtual machine was not provided.");
5609-
}
5610-
5611-
// Check if an SSH key pair was selected for the instance and if so
5612-
// use it to encrypt & save the vm password
5613-
encryptAndStorePassword(vm, password);
5614-
5615-
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password);
5616-
}
5617-
5618-
if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) {
5619-
if (!HypervisorType.VMware.equals(vm.getHypervisorType())) {
5620-
throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType());
5621-
}
5622-
Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup);
5623-
if (logger.isTraceEnabled()) {
5624-
logger.trace("It was specified whether to enter setup mode: " + paramValue.toString());
5625-
}
5626-
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue);
5627-
}
5628-
5629-
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
5630-
5631-
DeploymentPlanner planner = null;
5632-
if (deploymentPlannerToUse != null) {
5633-
// if set to null, the deployment planner would be later figured out either from global config var, or from
5634-
// the service offering
5635-
planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse);
5636-
if (planner == null) {
5637-
throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse);
5638-
}
5639-
}
5640-
vmEntity.setParamsToEntity(additionalParams);
5641-
5642-
String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId()));
5643-
vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost);
5652+
boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId());
56445653

5645-
vmParamPair = new Pair(vm, params);
5646-
if (vm != null && vm.isUpdateParameters()) {
5647-
// this value is not being sent to the backend; need only for api
5648-
// display purposes
5649-
if (template.isEnablePassword()) {
5650-
if (vm.getDetail(VmDetailConstants.PASSWORD) != null) {
5651-
userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD);
5652-
}
5653-
vm.setUpdateParameters(false);
5654-
_vmDao.update(vm.getId(), vm);
5655-
}
5654+
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
5655+
if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
5656+
ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
5657+
List<String> resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(offering, template);
5658+
try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, resourceLimitHostTags, 1l, reservationDao, resourceLimitService);
5659+
CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, resourceLimitHostTags, Long.valueOf(offering.getCpu()), reservationDao, resourceLimitService);
5660+
CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, resourceLimitHostTags, Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService);
5661+
) {
5662+
return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin);
5663+
} catch (ResourceAllocationException | CloudRuntimeException e) {
5664+
throw e;
5665+
} catch (Exception e) {
5666+
logger.error("Failed to start VM {} : error during resource reservation and allocation", e);
5667+
throw new CloudRuntimeException(e);
56565668
}
5657-
} catch (Exception e) {
5658-
logger.error("Failed to start VM {}", vm, e);
5659-
throw new CloudRuntimeException("Failed to start VM " + vm, e);
5669+
} else {
5670+
return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin);
56605671
}
5661-
return vmParamPair;
56625672
}
56635673

56645674
/**

0 commit comments

Comments
 (0)