This is an automated email from the ASF dual-hosted git repository. dahn pushed a commit to branch 4.20 in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 4bcd509193fcbcfdbeb88743b029584882aa7e30 Author: Abhisar Sinha <[email protected]> AuthorDate: Fri Feb 20 10:54:08 2026 +0530 Fix resource limit reservation and check during StartVirtualMachine --- .../main/java/com/cloud/vm/UserVmManagerImpl.java | 270 +++++++++++---------- 1 file changed, 140 insertions(+), 130 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0e1c3bb9194..7fcf242ea20 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5493,13 +5493,135 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return startVirtualMachine(vmId, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, true); } + private Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachineUnchecked(UserVmVO vm, VMTemplateVO template, Long podId, + Long clusterId, Long hostId, @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse, + boolean isExplicitHost, boolean isRootAdmin) throws ResourceUnavailableException, InsufficientCapacityException { + + // check if vm is security group enabled + if (_securityGroupMgr.isVmSecurityGroupEnabled(vm.getId()) && _securityGroupMgr.getSecurityGroupsForVm(vm.getId()).isEmpty() + && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vm.getId()) && _networkModel.canAddDefaultSecurityGroup()) { + // if vm is not mapped to security group, create a mapping + if (logger.isDebugEnabled()) { + logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); + } + + SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); + if (defaultSecurityGroup != null) { + List<Long> groupList = new ArrayList<>(); + groupList.add(defaultSecurityGroup.getId()); + _securityGroupMgr.addInstanceToGroups(vm, groupList); + } + } + + // Choose deployment planner + // Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd + // Default behaviour is invoked when host, cluster or pod are not specified + Pod destinationPod = getDestinationPod(podId, isRootAdmin); + Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin); + HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost); + DataCenterDeployment plan = null; + boolean deployOnGivenHost = false; + if (destinationHost != null) { + logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + _hostDao.loadHostTags(destinationHost); + validateStrictHostTagCheck(vm, destinationHost); + + final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false); + if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) { + String errorMsg; + if (!cpuCapabilityAndCapacity.first()) { + errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost); + } else { + 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); + } + logger.info(errorMsg); + if (!AllowDeployVmIfGivenHostFails.value()) { + throw new InvalidParameterValueException(errorMsg); + } + } else { + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } + } else if (destinationCluster != null) { + logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } else if (destinationPod != null) { + logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } + + // Set parameters + Map<VirtualMachineProfile.Param, Object> params = null; + if (vm.isUpdateParameters()) { + _vmDao.loadDetails(vm); + String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); + if (!validPassword(password)) { + throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); + } + // Check if an SSH key pair was selected for the instance and if so + // use it to encrypt & save the vm password + encryptAndStorePassword(vm, password); + params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); + } + + if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { + if (!HypervisorType.VMware.equals(vm.getHypervisorType())) { + throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); + } + Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); + if (logger.isTraceEnabled()) { + logger.trace("It was specified whether to enter setup mode: " + paramValue.toString()); + } + params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue); + } + + VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); + + DeploymentPlanner planner = null; + if (deploymentPlannerToUse != null) { + // if set to null, the deployment planner would be later figured out either from global config var, or from + // the service offering + planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); + if (planner == null) { + throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); + } + } + vmEntity.setParamsToEntity(additionalParams); + + UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); + String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); + vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); + + Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair = new Pair(vm, params); + if (vm.isUpdateParameters()) { + // this value is not being sent to the backend; need only for api + // display purposes + if (template.isEnablePassword()) { + if (vm.getDetail(VmDetailConstants.PASSWORD) != null) { + userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD); + } + vm.setUpdateParameters(false); + _vmDao.update(vm.getId(), vm); + } + } + return vmParamPair; + } + @Override public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse, boolean isExplicitHost) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { // Input validation final Account callerAccount = CallContext.current().getCallingAccount(); - UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); // if account is removed, return error if (callerAccount == null || callerAccount.getRemoved() != null) { @@ -5527,138 +5649,26 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (owner.getState() == Account.State.DISABLED) { throw new PermissionDeniedException(String.format("The owner of %s is disabled: %s", vm, owner)); } - Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair; - try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, vm.getId(), null, 1L, reservationDao, _resourceLimitMgr)) { - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - // check if account/domain is with in resource limits to start a new vm - ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - resourceLimitService.checkVmResourceLimit(owner, vm.isDisplayVm(), offering, template); - } - // check if vm is security group enabled - if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() - && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) { - // if vm is not mapped to security group, create a mapping - if (logger.isDebugEnabled()) { - logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); - } - - SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); - if (defaultSecurityGroup != null) { - List<Long> groupList = new ArrayList<>(); - groupList.add(defaultSecurityGroup.getId()); - _securityGroupMgr.addInstanceToGroups(vm, groupList); - } - } - // Choose deployment planner - // Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd - // Default behaviour is invoked when host, cluster or pod are not specified - boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); - Pod destinationPod = getDestinationPod(podId, isRootAdmin); - Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin); - HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost); - DataCenterDeployment plan = null; - boolean deployOnGivenHost = false; - if (destinationHost != null) { - logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - _hostDao.loadHostTags(destinationHost); - validateStrictHostTagCheck(vm, destinationHost); - - final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false); - if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) { - String errorMsg; - if (!cpuCapabilityAndCapacity.first()) { - errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost); - } else { - 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); - } - logger.info(errorMsg); - if (!AllowDeployVmIfGivenHostFails.value()) { - throw new InvalidParameterValueException(errorMsg); - } - } else { - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } - } else if (destinationCluster != null) { - logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } else if (destinationPod != null) { - logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } - - // Set parameters - Map<VirtualMachineProfile.Param, Object> params = null; - if (vm.isUpdateParameters()) { - _vmDao.loadDetails(vm); - - String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); - - if (!validPassword(password)) { - throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); - } - - // Check if an SSH key pair was selected for the instance and if so - // use it to encrypt & save the vm password - encryptAndStorePassword(vm, password); - - params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); - } - - if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { - if (!HypervisorType.VMware.equals(vm.getHypervisorType())) { - throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); - } - Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); - if (logger.isTraceEnabled()) { - logger.trace("It was specified whether to enter setup mode: " + paramValue.toString()); - } - params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue); - } - - VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - - DeploymentPlanner planner = null; - if (deploymentPlannerToUse != null) { - // if set to null, the deployment planner would be later figured out either from global config var, or from - // the service offering - planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); - if (planner == null) { - throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); - } - } - vmEntity.setParamsToEntity(additionalParams); - - String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); - vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); + boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); - vmParamPair = new Pair(vm, params); - if (vm != null && vm.isUpdateParameters()) { - // this value is not being sent to the backend; need only for api - // display purposes - if (template.isEnablePassword()) { - if (vm.getDetail(VmDetailConstants.PASSWORD) != null) { - userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD); - } - vm.setUpdateParameters(false); - _vmDao.update(vm.getId(), vm); - } + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + List<String> resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(offering, template); + try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, resourceLimitHostTags, 1l, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, resourceLimitHostTags, Long.valueOf(offering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, resourceLimitHostTags, Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService); + ) { + return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); + } catch (ResourceAllocationException | CloudRuntimeException e) { + throw e; + } catch (Exception e) { + logger.error("Failed to start VM {} : error during resource reservation and allocation", e); + throw new CloudRuntimeException(e); } - } catch (Exception e) { - logger.error("Failed to start VM {}", vm, e); - throw new CloudRuntimeException("Failed to start VM " + vm, e); + } else { + return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); } - return vmParamPair; } /**
