diff --git a/api/src/main/java/com/cloud/projects/ProjectService.java b/api/src/main/java/com/cloud/projects/ProjectService.java index 5080cb5a781..d11e9ae0446 100644 --- a/api/src/main/java/com/cloud/projects/ProjectService.java +++ b/api/src/main/java/com/cloud/projects/ProjectService.java @@ -82,7 +82,7 @@ public interface ProjectService { Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException; - boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType); + boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException; boolean deleteAccountFromProject(long projectId, String accountName); @@ -100,6 +100,6 @@ public interface ProjectService { Project findByProjectAccountIdIncludingRemoved(long projectAccountId); - boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole); + boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 93609555122..d725c4a967b 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -30,6 +30,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.template.VirtualMachineTemplate; +import org.apache.cloudstack.resourcelimit.Reserver; public interface ResourceLimitService { @@ -246,12 +247,12 @@ public interface ResourceLimitService { List getResourceLimitStorageTags(DiskOffering diskOffering); void updateTaggedResourceLimitsAndCountsForAccounts(List responses, String tag); void updateTaggedResourceLimitsAndCountsForDomains(List responses, String tag); - void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; + void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException; List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering); void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException; + DiskOffering currentOffering, DiskOffering newOffering, List reservations) throws ResourceAllocationException; - void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; + void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException; void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); @@ -268,20 +269,18 @@ public interface ResourceLimitService { void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); - void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException; + void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException; void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template); void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template); void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, - Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template) throws ResourceAllocationException; + Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException; void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException; + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException; - void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException; void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu); void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu); - void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException; void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java index 93021487040..6342709280a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.account; import java.util.List; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.BaseCmd; @@ -106,7 +107,7 @@ public class AddAccountToProjectCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Override - public void execute() { + public void execute() throws ResourceAllocationException { if (accountName == null && email == null) { throw new InvalidParameterValueException("Either accountName or email is required"); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java index 9bdc85bc5c7..0a2d8824a5b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.account; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; @@ -111,7 +112,7 @@ public class AddUserToProjectCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Override - public void execute() { + public void execute() throws ResourceAllocationException { validateInput(); boolean result = _projectService.addUserToProject(getProjectId(), getUsername(), getEmail(), getProjectRoleId(), getRoleType()); if (result) { diff --git a/server/src/main/java/com/cloud/resourcelimit/Reserver.java b/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java similarity index 73% rename from server/src/main/java/com/cloud/resourcelimit/Reserver.java rename to api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java index 4d5e3ac30c5..6b3f57b6aa5 100644 --- a/server/src/main/java/com/cloud/resourcelimit/Reserver.java +++ b/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java @@ -15,8 +15,14 @@ // specific language governing permissions and limitations // under the License. -package com.cloud.resourcelimit; +package org.apache.cloudstack.resourcelimit; +/** + * Interface implemented by CheckedReservation. + *

+ * This is defined in cloud-api to allow methods declared in modules that do not depend on cloud-server + * to receive CheckedReservations as parameters. + */ public interface Reserver extends AutoCloseable { void close(); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java index f100822b8c7..cd0390aa268 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.test; +import com.cloud.exception.ResourceAllocationException; import junit.framework.Assert; import junit.framework.TestCase; @@ -149,6 +150,8 @@ public class AddAccountToProjectCmdTest extends TestCase { addAccountToProjectCmd.execute(); } catch (InvalidParameterValueException exception) { Assert.assertEquals("Either accountName or email is required", exception.getLocalizedMessage()); + } catch (ResourceAllocationException exception) { + Assert.fail(); } } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index c0976fe137e..2dcb8fa2059 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -4757,9 +4757,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) { Map details = userVmDetailsDao.listDetailsKeyPairs(vmId); - details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); - details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); - details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); + + // We need to restore only the customizable parameters. If we save a parameter that is not customizable and attempt + // to restore a VM snapshot, com.cloud.vm.UserVmManagerImpl.validateCustomParameters will fail. + ServiceOffering unfilledOffering = _serviceOfferingDao.findByIdIncludingRemoved(serviceOffering.getId()); + if (unfilledOffering.getCpu() == null) { + details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); + } + if (unfilledOffering.getSpeed() == null) { + details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); + } + if (unfilledOffering.getRamSize() == null) { + details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); + } + List detailList = new ArrayList<>(); for (Map.Entry entry: details.entrySet()) { UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index f8bf613d3e7..4fd5cbd1949 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -3097,9 +3097,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra CallContext.current().setEventDetails("Network Id: " + network.getId()); CallContext.current().putContextParameter(Network.class, network.getUuid()); return network; - } catch (Exception e) { - logger.error(e); - throw new RuntimeException(e); } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index fdaec016cf5..0fc61d81588 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -39,6 +39,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.exception.ResourceAllocationException; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; @@ -77,6 +78,7 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.secret.PassphraseVO; import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.snapshot.SnapshotHelper; @@ -1867,14 +1869,20 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati template == null ? null : template.getSize(), vol.getPassphraseId() != null); - if (newSize != vol.getSize()) { - DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()); + if (newSize == vol.getSize()) { + return; + } + + DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()); + + List reservations = new ArrayList<>(); + try { VMInstanceVO vm = vol.getInstanceId() != null ? vmInstanceDao.findById(vol.getInstanceId()) : null; if (vm == null || vm.getType() == VirtualMachine.Type.User) { // Update resource count for user vm volumes when volume is attached if (newSize > vol.getSize()) { _resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()), - vol.isDisplay(), newSize - vol.getSize(), diskOffering); + vol.isDisplay(), newSize - vol.getSize(), diskOffering, reservations); _resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(), newSize - vol.getSize(), diskOffering); } else { @@ -1882,9 +1890,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati vol.getSize() - newSize, diskOffering); } } - vol.setSize(newSize); - _volsDao.persist(vol); + } finally { + ReservationHelper.closeAll(reservations); } + vol.setSize(newSize); + _volsDao.persist(vol); } @Override diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 8302f0ddf15..43efccd04f9 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -544,7 +544,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_USER_ADD, eventDescription = "adding user to project", async = true) - public boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) { + public boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); Project project = getProject(projectId); @@ -614,8 +614,6 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C logger.warn("Failed to add user to project: {}", project); return false; } - } catch (ResourceAllocationException e) { - throw new RuntimeException(e); } } } @@ -814,7 +812,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACCOUNT_ADD, eventDescription = "adding account to project", async = true) - public boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) { + public boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); //check that the project exists @@ -892,8 +890,6 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C logger.warn("Failed to add account {} to project {}", accountName, project); return false; } - } catch (ResourceAllocationException e) { - throw new RuntimeException(e); } } } diff --git a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java index 8d2e19d475e..5f9913e2ee5 100644 --- a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java +++ b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java @@ -20,13 +20,16 @@ package com.cloud.resourcelimit; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.user.ResourceReservation; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -48,12 +51,15 @@ public class CheckedReservation implements Reserver { ReservationDao reservationDao; ResourceLimitService resourceLimitService; - private final Account account; + private Account account; private Long domainId; - private final ResourceType resourceType; - private Long amount; + private ResourceType resourceType; + private Long resourceId; + private Long reservationAmount; + private List reservationTags; + private Long existingAmount; + private List existingLimitTags; private List reservations; - private List resourceLimitTags; private String getContextParameterKey() { return getResourceReservationContextParameterKey(resourceType); @@ -100,9 +106,25 @@ public class CheckedReservation implements Reserver { this.reservations.add(reservation); } - public CheckedReservation(Account account, ResourceType resourceType, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { - this(account, resourceType, null, resourceLimitTags, amount, reservationDao, resourceLimitService); + // TODO: refactor these into a Builder to avoid having so many constructors + public CheckedReservation(Account account, ResourceType resourceType, List resourceLimitTags, Long reservationAmount, + ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, resourceType, null, resourceLimitTags, null, reservationAmount, null, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List reservedTags, + List existingTags, Long reservationAmount, Long existingAmount, ReservationDao reservationDao, + ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, null, resourceType, resourceId, reservedTags, existingTags, reservationAmount, existingAmount, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List reservedTags, + Long reservationAmount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, domainId, resourceType, resourceId, reservedTags, null, reservationAmount, null, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List reservedTags, Long reservationAmount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, null, resourceType, resourceId, reservedTags, null, reservationAmount, null, reservationDao, resourceLimitService); } /** @@ -110,36 +132,43 @@ public class CheckedReservation implements Reserver { * - create DB entry for reservation * - hold the id of this record as a ticket for implementation * - * @param amount positive number of the resource type to reserve + * @param reservationAmount positive number of the resource type to reserve * @throws ResourceAllocationException */ - public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { - this(account, account.getDomainId(), resourceType, resourceId, resourceLimitTags, amount, reservationDao, resourceLimitService); - } + public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List reservedTags, + List existingTags, Long reservationAmount, Long existingAmount, ReservationDao reservationDao, + ResourceLimitService resourceLimitService) throws ResourceAllocationException { + + if (ObjectUtils.allNull(account, domainId)) { + logger.debug("Not reserving any {} resources, as no account/domain was provided.", resourceType); + return; + } - public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { this.reservationDao = reservationDao; this.resourceLimitService = resourceLimitService; this.account = account; - this.domainId = domainId; if (domainId == null) { - this.domainId = account.getDomainId(); + domainId = account.getDomainId(); } + this.domainId = domainId; this.resourceType = resourceType; - this.amount = amount; + this.reservationAmount = reservationAmount; + this.existingAmount = existingAmount; this.reservations = new ArrayList<>(); - this.resourceLimitTags = resourceLimitTags; - if (this.amount != null && this.amount != 0) { - if (amount > 0) { + this.reservationTags = getTagsWithoutNull(reservedTags); + this.existingLimitTags = getTagsWithoutNull(existingTags); + + // TODO: refactor me + if (this.reservationAmount != null && this.reservationAmount != 0) { + if (reservationAmount > 0) { setGlobalLock(); if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) { try { - checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, resourceLimitTags, amount); + adjustCountToNotConsiderExistingAmount(); + checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, reservationTags, reservationAmount); CallContext.current().putContextParameter(getContextParameterKey(), getIds()); } catch (NullPointerException npe) { throw new CloudRuntimeException("not enough means to check limits", npe); @@ -150,7 +179,7 @@ public class CheckedReservation implements Reserver { throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType); } } else { - checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, resourceLimitTags, amount); + checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, reservationTags, reservationAmount); } } else { logger.debug("not reserving any amount of resources for {} in domain {}, type: {}, tag: {}", @@ -158,6 +187,20 @@ public class CheckedReservation implements Reserver { } } + protected List getTagsWithoutNull(List tags) { + if (tags == null) { + return null; + } + return tags.stream().filter(Objects::nonNull).collect(Collectors.toList()); + } + + protected void adjustCountToNotConsiderExistingAmount() throws ResourceAllocationException { + if (existingAmount == null || existingAmount == 0) { + return; + } + checkLimitAndPersistReservations(account, domainId, resourceType, resourceId, existingLimitTags, -1 * existingAmount); + } + public CheckedReservation(Account account, ResourceType resourceType, Long amount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { this(account, resourceType, null, amount, reservationDao, resourceLimitService); @@ -183,11 +226,11 @@ public class CheckedReservation implements Reserver { } public String getResourceLimitTagsAsString() { - return CollectionUtils.isNotEmpty(resourceLimitTags) ? StringUtils.join(resourceLimitTags) : null; + return CollectionUtils.isNotEmpty(reservationTags) ? StringUtils.join(reservationTags) : null; } public Long getReservedAmount() { - return amount; + return reservationAmount; } public List getReservations() { diff --git a/server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java b/server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java new file mode 100644 index 00000000000..cffa17176fa --- /dev/null +++ b/server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java @@ -0,0 +1,35 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.resourcelimit; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.resourcelimit.Reserver; + +import java.util.List; + +public class ReservationHelper { + + public static void closeAll(List reservations) throws CloudRuntimeException { + for (Reserver reservation : reservations) { + reservation.close(); + } + } + +} diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 43c3b383258..6d2ec103ca2 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -52,6 +52,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -168,6 +169,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Inject private ReservationDao reservationDao; @Inject + private ResourceLimitService resourceLimitService; + @Inject protected SnapshotDao _snapshotDao; @Inject private SnapshotDataStoreDao _snapshotDataStoreDao; @@ -1662,54 +1665,53 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } @Override - public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException { + public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException { List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); if (CollectionUtils.isEmpty(tags)) { return; } - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.volume, tag); - if (size != null) { - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size); - } + + CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, tags, 1L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + if (size != null) { + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } } @Override - public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException { + public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException { List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); if (CollectionUtils.isEmpty(tags)) { return; } - if (size != null) { - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size); - } - } + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } @Override public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering + DiskOffering currentOffering, DiskOffering newOffering, List reservations ) throws ResourceAllocationException { Ternary, Set, Set> updatedResourceLimitStorageTags = getResourceLimitStorageTagsForDiskOfferingChange(display, currentOffering, newOffering); if (updatedResourceLimitStorageTags == null) { return; } - Set sameTags = updatedResourceLimitStorageTags.first(); - Set newTags = updatedResourceLimitStorageTags.second(); - - if (newSize > currentSize) { - for (String tag : sameTags) { - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize - currentSize); - } + List currentTags = getResourceLimitStorageTagsForResourceCountOperation(true, currentOffering); + List tagsAfterUpdate = getResourceLimitStorageTagsForResourceCountOperation(true, newOffering); + if (currentTags.isEmpty() && tagsAfterUpdate.isEmpty()) { + return; } - for (String tag : newTags) { - checkResourceLimitWithTag(owner, ResourceType.volume, tag, 1L); - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize); - } + CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, null, tagsAfterUpdate, + currentTags, 1L, 1L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, null, + tagsAfterUpdate, currentTags, newSize, currentSize, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } @DB @@ -1932,18 +1934,23 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } @Override - public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException { + public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); if (CollectionUtils.isEmpty(tags)) { return; } + + CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, tags, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, tags, cpu, reservationDao, resourceLimitService); + reservations.add(cpuReservation); + Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.user_vm, tag); - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu); - checkResourceLimitWithTag(owner, ResourceType.memory, tag, ram); - } + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, tags, ram, reservationDao, resourceLimitService); + reservations.add(memReservation); + } @Override @@ -1989,76 +1996,53 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Override public void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException { + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException { checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, null, null, - null, null, offering, offering, currentTemplate, newTemplate); + null, null, offering, offering, currentTemplate, newTemplate, reservations); } @Override public void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, Long newMemory, - ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template + ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List reservations ) throws ResourceAllocationException { checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, currentCpu, newCpu, currentMemory, newMemory, currentOffering, - newOffering != null ? newOffering : currentOffering, template, template); + newOffering != null ? newOffering : currentOffering, template, template, reservations); } private void checkVmResourceLimitsForServiceOfferingAndTemplateChange(Account owner, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations ) throws ResourceAllocationException { - Ternary, Set, Set> updatedResourceLimitHostTags = getResourceLimitHostTagsForVmServiceOfferingAndTemplateChange(display, currentOffering, newOffering, currentTemplate, newTemplate); - if (updatedResourceLimitHostTags == null) { + List currentTags = getResourceLimitHostTagsForResourceCountOperation(true, currentOffering, currentTemplate); + List tagsAfterUpdate = getResourceLimitHostTagsForResourceCountOperation(true, newOffering, newTemplate); + if (currentTags.isEmpty() && tagsAfterUpdate.isEmpty()) { return; } + CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, null, tagsAfterUpdate, + currentTags, 1L, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + if (currentCpu == null) { currentCpu = currentOffering.getCpu() != null ? Long.valueOf(currentOffering.getCpu()) : 0L; } if (newCpu == null) { newCpu = newOffering.getCpu() != null ? Long.valueOf(newOffering.getCpu()) : 0L; } + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, null, tagsAfterUpdate, + currentTags, newCpu, currentCpu, reservationDao, resourceLimitService); + reservations.add(cpuReservation); + if (currentMemory == null) { currentMemory = currentOffering.getRamSize() != null ? Long.valueOf(currentOffering.getRamSize()) : 0L; } if (newMemory == null) { newMemory = newOffering.getRamSize() != null ? Long.valueOf(newOffering.getRamSize()) : 0L; } - - Set sameTags = updatedResourceLimitHostTags.first(); - Set newTags = updatedResourceLimitHostTags.second(); - - if (newCpu - currentCpu > 0 || newMemory - currentMemory > 0) { - for (String tag : sameTags) { - if (newCpu - currentCpu > 0) { - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu - currentCpu); - } - - if (newMemory - currentMemory > 0) { - checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory - currentMemory); - } - } - } - - for (String tag : newTags) { - checkResourceLimitWithTag(owner, ResourceType.user_vm, tag, 1L); - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu); - checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory); - } - } - - @Override - public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException { - List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); - if (CollectionUtils.isEmpty(tags)) { - return; - } - if (cpu == null) { - cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; - } - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu); - } + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, null, tagsAfterUpdate, + currentTags, newMemory, currentMemory, reservationDao, resourceLimitService); + reservations.add(memReservation); } @Override @@ -2089,20 +2073,6 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } } - @Override - public void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException { - List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); - if (CollectionUtils.isEmpty(tags)) { - return; - } - if (memory == null) { - memory = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; - } - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.memory, tag, memory); - } - } - @Override public void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) { List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 884ece90b1c..7186b07334d 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -36,6 +36,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.resourcelimit.ReservationHelper; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.InternalIdentity; @@ -93,6 +94,7 @@ import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; @@ -425,9 +427,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Long diskOfferingId = cmd.getDiskOfferingId(); String imageStoreUuid = cmd.getImageStoreUuid(); - validateVolume(caller, ownerId, zoneId, volumeName, url, format, diskOfferingId); + VolumeVO volume; - VolumeVO volume = persistVolume(owner, zoneId, volumeName, url, format, diskOfferingId, Volume.State.Allocated); + List reservations = new ArrayList<>(); + try { + + validateVolume(caller, ownerId, zoneId, volumeName, url, format, diskOfferingId, reservations); + volume = persistVolume(owner, zoneId, volumeName, url, format, diskOfferingId, Volume.State.Allocated); + + } finally { + ReservationHelper.closeAll(reservations); + } VolumeInfo vol = volFactory.getVolume(volume.getId()); @@ -467,7 +477,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic final Long diskOfferingId = cmd.getDiskOfferingId(); String imageStoreUuid = cmd.getImageStoreUuid(); - validateVolume(caller, ownerId, zoneId, volumeName, null, format, diskOfferingId); + List reservations = new ArrayList<>(); + try { + validateVolume(caller, ownerId, zoneId, volumeName, null, format, diskOfferingId, reservations); return Transaction.execute(new TransactionCallbackWithException() { @Override @@ -535,9 +547,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return response; } }); + + } finally { + ReservationHelper.closeAll(reservations); + } } - private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format, Long diskOfferingId) throws ResourceAllocationException { + private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format, Long diskOfferingId, List reservations) throws ResourceAllocationException { // permission check Account volumeOwner = _accountMgr.getActiveAccountById(ownerId); @@ -548,7 +564,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _accountMgr.checkAccess(caller, null, true, volumeOwner); // Check that the resource limit for volumes won't be exceeded - _resourceLimitMgr.checkVolumeResourceLimit(volumeOwner, true, null, diskOffering); + _resourceLimitMgr.checkVolumeResourceLimit(volumeOwner, true, null, diskOffering, reservations); // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); @@ -926,8 +942,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (tags.size() == 1 && tags.get(0) == null) { tags = new ArrayList<>(); } - try (CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, null, tags, 1L, reservationDao, _resourceLimitMgr); - CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, null, tags, size, reservationDao, _resourceLimitMgr)) { + + List reservations = new ArrayList<>(); + try { + _resourceLimitMgr.checkVolumeResourceLimit(owner, displayVolume, size, diskOffering, reservations); // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); @@ -950,9 +968,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return commitVolume(cmd, caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName, _uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details); - } catch (Exception e) { - logger.error(e); - throw new RuntimeException(e); + } finally { + ReservationHelper.closeAll(reservations); } } @@ -1278,7 +1295,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (dataStore != null && dataStore.getDriver() instanceof PrimaryDataStoreDriver) { newSize = ((PrimaryDataStoreDriver) dataStore.getDriver()).getVolumeSizeRequiredOnPool(newSize, null, isEncryptionRequired); } - validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering); + + List reservations = new ArrayList<>(); + try { + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering, reservations); // Note: The storage plug-in in question should perform validation on the IOPS to check if a sufficient number of IOPS is available to perform // the requested change @@ -1406,6 +1426,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + + } finally { + ReservationHelper.closeAll(reservations); + } } /** @@ -1839,12 +1863,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Please specify a volume in Destroy state."); } + DiskOffering diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + + List reservations = new ArrayList<>(); try { - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize()); - } catch (ResourceAllocationException e) { - logger.error("primary storage resource limit check failed", e); - throw new InvalidParameterValueException(e.getMessage()); - } + _resourceLimitMgr.checkVolumeResourceLimit(_accountMgr.getAccount(volume.getAccountId()), volume.isDisplayVolume(), volume.getSize(), diskOffering, reservations); try { _volsDao.detachVolume(volume.getId()); @@ -1856,6 +1879,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _resourceLimitMgr.incrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(), volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId())); + } catch (ResourceAllocationException e) { + logger.error("primary storage resource limit check failed", e); + throw new InvalidParameterValueException(e.getMessage()); + } finally { + ReservationHelper.closeAll(reservations); + } publishVolumeCreationUsageEvent(volume); @@ -2085,7 +2114,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic newSize = ((PrimaryDataStoreDriver) dataStore.getDriver()).getVolumeSizeRequiredOnPool(newSize, null, newDiskOffering.getEncrypt()); } - validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering); + List reservations = new ArrayList<>(); + try { + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering, reservations); /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ // We need to publish this event to usage_volume table @@ -2175,6 +2206,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } return volume; + + } finally { + ReservationHelper.closeAll(reservations); + } } private void updateStorageWithTheNewDiskOffering(VolumeVO volume, DiskOfferingVO newDiskOffering) { @@ -2380,7 +2415,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } private void validateVolumeResizeWithSize(VolumeVO volume, long currentSize, Long newSize, boolean shrinkOk, - DiskOfferingVO existingDiskOffering, DiskOfferingVO newDiskOffering) throws ResourceAllocationException { + DiskOfferingVO existingDiskOffering, DiskOfferingVO newDiskOffering, List reservations) throws ResourceAllocationException { // if the caller is looking to change the size of the volume if (newSize != null && currentSize != newSize) { @@ -2450,7 +2485,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic /* Check resource limit for this account */ _resourceLimitMgr.checkVolumeResourceLimitForDiskOfferingChange(_accountMgr.getAccount(volume.getAccountId()), volume.isDisplayVolume(), currentSize, newSize != null ? newSize : currentSize, - existingDiskOffering, newDiskOffering); + existingDiskOffering, newDiskOffering, reservations); } @Override @@ -2622,7 +2657,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic checkForBackups(vm, true); - checkRightsToAttach(caller, volumeToAttach, vm); + _accountMgr.checkAccess(caller, null, true, volumeToAttach, vm); HypervisorType rootDiskHyperType = vm.getHypervisorType(); HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId()); @@ -2649,6 +2684,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Volume's disk offering has encryption enabled, but volume encryption is not supported for hypervisor type " + rootDiskHyperType); } + Account owner = _accountDao.findById(volumeToAttach.getAccountId()); + List resourceLimitStorageTags = _resourceLimitMgr.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); + Long requiredPrimaryStorageSpace = getRequiredPrimaryStorageSizeForVolumeAttach(resourceLimitStorageTags, volumeToAttach); + + try (CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, resourceLimitStorageTags, requiredPrimaryStorageSpace, reservationDao, _resourceLimitMgr)) { + _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); if (asyncExecutionContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { @@ -2656,9 +2697,21 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } else { return getVolumeAttachJobResult(vmId, volumeId, deviceId); } + + } catch (ResourceAllocationException e) { + logger.error("primary storage resource limit check failed", e); + throw new InvalidParameterValueException(e.getMessage()); + } } - @Nullable private Volume getVolumeAttachJobResult(Long vmId, Long volumeId, Long deviceId) { + protected Long getRequiredPrimaryStorageSizeForVolumeAttach(List resourceLimitStorageTags, VolumeInfo volumeToAttach) { + if (CollectionUtils.isEmpty(resourceLimitStorageTags) || Arrays.asList(Volume.State.Allocated, Volume.State.Ready).contains(volumeToAttach.getState())) { + return 0L; + } + return volumeToAttach.getSize(); + } + + @Nullable protected Volume getVolumeAttachJobResult(Long vmId, Long volumeId, Long deviceId) { Outcome outcome = attachVolumeToVmThroughJobQueue(vmId, volumeId, deviceId); Volume vol = null; @@ -2707,21 +2760,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - private void checkRightsToAttach(Account caller, VolumeInfo volumeToAttach, UserVmVO vm) { - _accountMgr.checkAccess(caller, null, true, volumeToAttach, vm); - - Account owner = _accountDao.findById(volumeToAttach.getAccountId()); - - if (!Arrays.asList(Volume.State.Allocated, Volume.State.Ready).contains(volumeToAttach.getState())) { - try { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, volumeToAttach.getSize()); - } catch (ResourceAllocationException e) { - logger.error("primary storage resource limit check failed", e); - throw new InvalidParameterValueException(e.getMessage()); - } - } - } - private void checkForVMSnapshots(Long vmId, UserVmVO vm) { // if target VM has associated VM snapshots List vmSnapshots = _vmSnapshotDao.findByVm(vmId); @@ -4207,7 +4245,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _accountMgr.checkAccess(caller, null, true, oldAccount); _accountMgr.checkAccess(caller, null, true, newAccount); - _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId())); + List reservations = new ArrayList<>(); + try { + _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()), reservations); Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -4217,6 +4257,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic }); return volume; + + } finally { + ReservationHelper.closeAll(reservations); + } } protected void updateVolumeAccount(Account oldAccount, VolumeVO volume, Account newAccount) { diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index d5475948c59..4a4a7544ce7 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1759,16 +1759,13 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); return snapshot; - } catch (Exception e) { - if (e instanceof ResourceAllocationException) { - if (snapshotType != Type.MANUAL) { - String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); - logger.warn(msg); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, msg + ". Please, use updateResourceLimit to increase the limit"); - } - throw (ResourceAllocationException) e; + } catch (ResourceAllocationException e) { + if (snapshotType != Type.MANUAL) { + String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); + logger.warn(msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, msg + ". Please, use updateResourceLimit to increase the limit"); } - throw new CloudRuntimeException(e); + throw e; } } diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 5c34e2a7473..1f3eb567c6f 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -357,6 +357,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // Secondary storage resource count is not incremented for BareMetalTemplateAdapter // Note: checking the file size before registering will require the Management Server host to have access to the Internet and a DNS server + // If it does not, UriUtils.getRemoteSize will return 0L. long secondaryStorageUsage = adapter instanceof HypervisorTemplateAdapter && !cmd.isDirectDownload() ? UriUtils.getRemoteSize(cmd.getUrl(), StorageManager.DataStoreDownloadFollowRedirects.value()) : 0L; @@ -365,7 +366,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, TemplateProfile profile = adapter.prepare(cmd); VMTemplateVO template = adapter.create(profile); - // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); if (secondaryStorageUsage > 0) { _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage); @@ -405,7 +406,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, TemplateProfile profile = adapter.prepare(cmd); VMTemplateVO template = adapter.create(profile); - // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack // for HypervisorTemplateAdapter _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); if (secondaryStorageUsage > 0) { diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 7fcf242ea20..8b77cb506a8 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -54,6 +54,7 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; +import com.cloud.resourcelimit.ReservationHelper; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -122,6 +123,7 @@ import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.DettachCommand; @@ -1351,9 +1353,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Account owner = _accountMgr.getActiveAccountById(vmInstance.getAccountId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + + List reservations = new ArrayList<>(); + try { if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); } // Check that the specified service offering ID is valid @@ -1376,6 +1381,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return _vmDao.findById(vmInstance.getId()); + } finally { + ReservationHelper.closeAll(reservations); + } } /** @@ -2063,9 +2071,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + List reservations = new ArrayList<>(); + try { // Check resource limits _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); // Dynamically upgrade the running vms boolean success = false; @@ -2137,6 +2147,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } return success; + + } finally { + ReservationHelper.closeAll(reservations); + } } protected void validateDiskOfferingChecks(ServiceOfferingVO currentServiceOffering, ServiceOfferingVO newServiceOffering) { @@ -2322,10 +2336,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + List reservations = new ArrayList<>(); + try { // First check that the maximum number of UserVMs, CPU and Memory limit for the given // accountId will not be exceeded if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template); + resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations); } _haMgr.cancelDestroy(vm, vm.getHostId()); @@ -2350,6 +2366,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //Update Resource Count for the given account resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template); + + } finally { + ReservationHelper.closeAll(reservations); + } } }); @@ -2776,27 +2796,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir long currentCpu = currentServiceOffering.getCpu(); long currentMemory = currentServiceOffering.getRamSize(); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + List reservations = new ArrayList<>(); try { + _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), currentCpu, newCpu, + currentMemory, newMemory, currentServiceOffering, svcOffering, template, reservations); if (newCpu > currentCpu) { - _resourceLimitMgr.checkVmCpuResourceLimit(owner, vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu); + _resourceLimitMgr.incrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu); + } else if (newCpu > 0 && currentCpu > newCpu){ + _resourceLimitMgr.decrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentCpu - newCpu); } if (newMemory > currentMemory) { - _resourceLimitMgr.checkVmMemoryResourceLimit(owner, vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory); + _resourceLimitMgr.incrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory); + } else if (newMemory > 0 && currentMemory > newMemory){ + _resourceLimitMgr.decrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentMemory - newMemory); } } catch (ResourceAllocationException e) { logger.error(String.format("Failed to updated VM due to: %s", e.getLocalizedMessage())); throw new InvalidParameterValueException(e.getLocalizedMessage()); - } - - if (newCpu > currentCpu) { - _resourceLimitMgr.incrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu); - } else if (newCpu > 0 && currentCpu > newCpu){ - _resourceLimitMgr.decrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentCpu - newCpu); - } - if (newMemory > currentMemory) { - _resourceLimitMgr.incrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory); - } else if (newMemory > 0 && currentMemory > newMemory){ - _resourceLimitMgr.decrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentMemory - newMemory); + } finally { + ReservationHelper.closeAll(reservations); } } @@ -4236,7 +4254,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException(String.format("Invalid disk offering %s specified for datadisk Template %s. Disk offering size should be greater than or equal to the Template size", dataDiskOffering, dataDiskTemplate)); } _templateDao.loadDetails(dataDiskTemplate); - resourceLimitService.checkVolumeResourceLimit(owner, true, dataDiskOffering.getDiskSize(), dataDiskOffering); } } @@ -5660,11 +5677,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir 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); } } else { return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); @@ -7387,38 +7399,29 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return findMigratedVm(vm.getId(), vm.getType()); } - protected void checkVolumesLimits(Account account, List volumes) throws ResourceAllocationException { - Long totalVolumes = 0L; - Long totalVolumesSize = 0L; + protected void checkVolumesLimits(Account account, List volumes, List reservations) throws ResourceAllocationException { Map> diskOfferingTagsMap = new HashMap<>(); - Map tagVolumeCountMap = new HashMap<>(); - Map tagSizeMap = new HashMap<>(); + for (VolumeVO volume : volumes) { if (!volume.isDisplay()) { continue; } - totalVolumes++; - totalVolumesSize += volume.getSize(); - if (!diskOfferingTagsMap.containsKey(volume.getDiskOfferingId())) { - diskOfferingTagsMap.put(volume.getDiskOfferingId(), _resourceLimitMgr.getResourceLimitStorageTags( - _diskOfferingDao.findById(volume.getDiskOfferingId()))); + + Long diskOfferingId = volume.getDiskOfferingId(); + if (!diskOfferingTagsMap.containsKey(diskOfferingId)) { + DiskOffering diskOffering = _diskOfferingDao.findById(diskOfferingId); + List tagsForDiskOffering = _resourceLimitMgr.getResourceLimitStorageTags(diskOffering); + diskOfferingTagsMap.put(diskOfferingId, tagsForDiskOffering); } - List tags = diskOfferingTagsMap.get(volume.getDiskOfferingId()); - for (String tag : tags) { - if (tagVolumeCountMap.containsKey(tag)) { - tagVolumeCountMap.put(tag, tagVolumeCountMap.get(tag) + 1); - tagSizeMap.put(tag, tagSizeMap.get(tag) + volume.getSize()); - } else { - tagVolumeCountMap.put(tag, 1L); - tagSizeMap.put(tag, volume.getSize()); - } - } - } - _resourceLimitMgr.checkResourceLimit(account, ResourceType.volume, totalVolumes); - _resourceLimitMgr.checkResourceLimit(account, ResourceType.primary_storage, totalVolumesSize); - for (String tag : tagVolumeCountMap.keySet()) { - resourceLimitService.checkResourceLimitWithTag(account, ResourceType.volume, tag, tagVolumeCountMap.get(tag)); - resourceLimitService.checkResourceLimitWithTag(account, ResourceType.primary_storage, tag, tagSizeMap.get(tag)); + + List tags = diskOfferingTagsMap.get(diskOfferingId); + + CheckedReservation volumeReservation = new CheckedReservation(account, ResourceType.volume, tags, 1L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + long size = ObjectUtils.defaultIfNull(volume.getSize(), 0L); + CheckedReservation primaryStorageReservation = new CheckedReservation(account, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } } @@ -7460,14 +7463,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir final ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); VirtualMachineTemplate template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - verifyResourceLimitsForAccountAndStorage(newAccount, vm, offering, volumes, template); - validateIfNewOwnerHasAccessToTemplate(vm, newAccount, template); DomainVO domain = _domainDao.findById(domainId); logger.trace("Verifying if the new account [{}] has access to the specified domain [{}].", newAccount, domain); _accountMgr.checkAccess(newAccount, domain); + List reservations = new ArrayList<>(); + try { + verifyResourceLimitsForAccountAndStorage(newAccount, vm, offering, volumes, template, reservations); + Network newNetwork = ensureDestinationNetwork(cmd, vm, newAccount); try { Transaction.execute(new TransactionCallbackNoReturn() { @@ -7484,6 +7489,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw e; } + } finally { + ReservationHelper.closeAll(reservations); + } + logger.info("VM [{}] now belongs to account [{}].", vm.getInstanceName(), newAccountName); return vm; } @@ -7554,18 +7563,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir * @param volumes The volumes whose total size can exceed resource limits. * @throws ResourceAllocationException */ - protected void verifyResourceLimitsForAccountAndStorage(Account account, UserVmVO vm, ServiceOfferingVO offering, List volumes, VirtualMachineTemplate template) + protected void verifyResourceLimitsForAccountAndStorage(Account account, UserVmVO vm, ServiceOfferingVO offering, List volumes, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { logger.trace("Verifying if CPU and RAM for VM [{}] do not exceed account [{}] limit.", vm, account); if (!countOnlyRunningVmsInResourceLimitation()) { - resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), offering, template); + resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), offering, template, reservations); } logger.trace("Verifying if volume size for VM [{}] does not exceed account [{}] limit.", vm, account); - checkVolumesLimits(account, volumes); + checkVolumesLimits(account, volumes, reservations); } protected boolean countOnlyRunningVmsInResourceLimitation() { @@ -8415,12 +8424,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VMTemplateVO template = getRestoreVirtualMachineTemplate(caller, newTemplateId, rootVols, vm); DiskOffering diskOffering = rootDiskOfferingId != null ? _diskOfferingDao.findById(rootDiskOfferingId) : null; + + List reservations = new ArrayList<>(); try { - checkRestoreVmFromTemplate(vm, template, rootVols, diskOffering, details); - } catch (ResourceAllocationException e) { - logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e); - throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e); - } + checkRestoreVmFromTemplate(vm, template, rootVols, diskOffering, details, reservations); if (needRestart) { try { @@ -8565,6 +8572,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir logger.debug("Restore VM {} done successfully", vm); return vm; + } catch (ResourceAllocationException e) { + logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e); + throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e); + } finally { + ReservationHelper.closeAll(reservations); + } } Long getRootVolumeSizeForVmRestore(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Map details, boolean update) { @@ -8664,7 +8677,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir * @param template template * @throws InvalidParameterValueException if restore is not possible */ - private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List rootVolumes, DiskOffering newDiskOffering, Map details) throws ResourceAllocationException { + private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List rootVolumes, DiskOffering newDiskOffering, Map details, List reservations) throws ResourceAllocationException { TemplateDataStoreVO tmplStore; if (!template.isDirectDownload()) { tmplStore = _templateStoreDao.findByTemplateZoneReady(template.getId(), vm.getDataCenterId()); @@ -8682,7 +8695,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vm.getTemplateId() != template.getId()) { ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); VMTemplateVO currentTemplate = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - _resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, vm.isDisplay(), serviceOffering, currentTemplate, template); + _resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, vm.isDisplay(), serviceOffering, currentTemplate, template, reservations); } for (Volume vol : rootVolumes) { @@ -8693,7 +8706,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (newDiskOffering != null || !vol.getSize().equals(newSize)) { DiskOffering currentOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); _resourceLimitMgr.checkVolumeResourceLimitForDiskOfferingChange(owner, vol.isDisplay(), - vol.getSize(), newSize, currentOffering, newDiskOffering); + vol.getSize(), newSize, currentOffering, newDiskOffering, reservations); } } } diff --git a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index cdbb7119e9e..8e7ce351246 100644 --- a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -811,25 +811,31 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme } /** - * If snapshot was taken with a different service offering than actual used in vm, should change it back to it - * @param userVm vm to change service offering (if necessary) + * If snapshot was taken with a different service offering than actual used in vm, should change it back to it. + * We also call changeUserVmServiceOffering in case the service offering is dynamic in order to + * perform resource limit validation, as the amount of CPUs or memory may have been changed. * @param vmSnapshotVo vm snapshot */ protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) { changeUserVmServiceOffering(userVm, vmSnapshotVo); + return; + } + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(userVm.getServiceOfferingId()); + if (serviceOffering.isDynamic()) { + changeUserVmServiceOffering(userVm, vmSnapshotVo); } } /** * Get user vm details as a map - * @param userVm user vm + * @param vmSnapshotVo snapshot to get the details from * @return map */ - protected Map getVmMapDetails(UserVm userVm) { - List userVmDetails = _userVmDetailsDao.listDetails(userVm.getId()); + protected Map getVmMapDetails(VMSnapshotVO vmSnapshotVo) { + List vmSnapshotDetails = _vmSnapshotDetailsDao.listDetails(vmSnapshotVo.getId()); Map details = new HashMap(); - for (UserVmDetailVO detail : userVmDetails) { + for (VMSnapshotDetailsVO detail : vmSnapshotDetails) { details.put(detail.getName(), detail.getValue()); } return details; @@ -841,7 +847,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme * @param vmSnapshotVo vm snapshot */ protected void changeUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { - Map vmDetails = getVmMapDetails(userVm); + Map vmDetails = getVmMapDetails(vmSnapshotVo); boolean result = upgradeUserVmServiceOffering(userVm, vmSnapshotVo.getServiceOfferingId(), vmDetails); if (! result){ throw new CloudRuntimeException("Instance Snapshot reverting failed because the Instance service offering couldn't be changed to the one used when Snapshot was taken"); @@ -938,8 +944,8 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException { - revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo); updateUserVmServiceOffering(userVm, vmSnapshotVo); + revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo); } }); return userVm; diff --git a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java index 9e1fc46e02e..47aa57f728c 100644 --- a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java @@ -35,6 +35,7 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.offering.DiskOffering; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage; @@ -68,6 +69,7 @@ import org.apache.cloudstack.api.response.VolumeForImportResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; @@ -198,10 +200,7 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ logFailureAndThrowException("Volume is a reference of snapshot on primary: " + volume.getFullPath()); } - // 5. check resource limitation - checkResourceLimitForImportVolume(owner, volume); - - // 6. get disk offering + // 5. get disk offering DiskOfferingVO diskOffering = getOrCreateDiskOffering(owner, cmd.getDiskOfferingId(), pool.getDataCenterId(), pool.isLocal()); if (diskOffering.isCustomized()) { volumeApiService.validateCustomDiskOfferingSizeRange(volume.getVirtualSize() / ByteScaleUtils.GiB); @@ -210,6 +209,11 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ logFailureAndThrowException(String.format("Disk offering: %s storage tags are not compatible with selected storage pool: %s", diskOffering, pool)); } + List reservations = new ArrayList<>(); + try { + // 6. check resource limitation + checkResourceLimitForImportVolume(owner, volume, diskOffering, reservations); + // 7. create records String volumeName = StringUtils.isNotBlank(cmd.getName()) ? cmd.getName().trim() : volumePath; VolumeVO volumeVO = importVolumeInternal(volume, diskOffering, owner, pool, volumeName); @@ -221,6 +225,10 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ publicUsageEventForVolumeImportAndUnmanage(volumeVO, true); return responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Full, volumeVO); + + } finally { + ReservationHelper.closeAll(reservations); + } } protected VolumeOnStorageTO getVolumeOnStorageAndCheck(StoragePoolVO pool, String volumePath) { @@ -456,11 +464,10 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ return volumeDao.findById(diskProfile.getVolumeId()); } - protected void checkResourceLimitForImportVolume(Account owner, VolumeOnStorageTO volume) { + protected void checkResourceLimitForImportVolume(Account owner, VolumeOnStorageTO volume, DiskOfferingVO diskOffering, List reservations) { Long volumeSize = volume.getVirtualSize(); try { - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.volume); - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.primary_storage, volumeSize); + resourceLimitService.checkVolumeResourceLimit(owner, true, volumeSize, diskOffering, reservations); } catch (ResourceAllocationException e) { logger.error("VM resource allocation error for account: {}", owner, e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 0cf921f36be..1917804fb8d 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -86,6 +86,8 @@ import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.org.Cluster; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; +import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.serializer.GsonHelper; import com.cloud.server.ManagementService; import com.cloud.service.ServiceOfferingVO; @@ -164,6 +166,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -222,6 +226,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { @Inject private ResourceLimitService resourceLimitService; @Inject + private ReservationDao reservationDao; + @Inject private UserVmDetailsDao userVmDetailsDao; @Inject private UserVmManager userVmManager; @@ -604,7 +610,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return new Pair<>(rootDisk, dataDisks); } - private void checkUnmanagedDiskAndOfferingForImport(String instanceName, UnmanagedInstanceTO.Disk disk, DiskOffering diskOffering, ServiceOffering serviceOffering, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed) + private void checkUnmanagedDiskAndOfferingForImport(String instanceName, UnmanagedInstanceTO.Disk disk, DiskOffering diskOffering, ServiceOffering serviceOffering, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed, List reservations) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { if (serviceOffering == null && diskOffering == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Disk offering for disk ID [%s] not found during VM [%s] import.", disk.getDiskId(), instanceName)); @@ -612,7 +618,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (diskOffering != null) { accountService.checkAccess(owner, diskOffering, zone); } - resourceLimitService.checkVolumeResourceLimit(owner, true, null, diskOffering); if (disk.getCapacity() == null || disk.getCapacity() == 0) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Size of disk(ID: %s) is found invalid during VM import", disk.getDiskId())); } @@ -627,9 +632,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (diskOffering != null && !migrateAllowed && !storagePoolSupportsDiskOffering(storagePool, diskOffering)) { throw new InvalidParameterValueException(String.format("Disk offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", diskOffering.getUuid(), storagePool.getUuid(), disk.getDiskId())); } + resourceLimitService.checkVolumeResourceLimit(owner, true, disk.getCapacity(), diskOffering, reservations); } - private void checkUnmanagedDiskAndOfferingForImport(String intanceName, List disks, final Map diskOfferingMap, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed) + private void checkUnmanagedDiskAndOfferingForImport(String intanceName, List disks, final Map diskOfferingMap, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed, List reservations) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { String diskController = null; for (UnmanagedInstanceTO.Disk disk : disks) { @@ -646,7 +652,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Multiple data disk controllers of different type (%s, %s) are not supported for import. Please make sure that all data disk controllers are of the same type", diskController, disk.getController())); } } - checkUnmanagedDiskAndOfferingForImport(intanceName, disk, diskOfferingDao.findById(diskOfferingMap.get(disk.getDiskId())), null, owner, zone, cluster, migrateAllowed); + checkUnmanagedDiskAndOfferingForImport(intanceName, disk, diskOfferingDao.findById(diskOfferingMap.get(disk.getDiskId())), null, owner, zone, cluster, migrateAllowed, reservations); } } @@ -1073,40 +1079,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } - protected void checkUnmanagedDiskLimits(Account account, UnmanagedInstanceTO.Disk rootDisk, ServiceOffering serviceOffering, - List dataDisks, Map dataDiskOfferingMap) throws ResourceAllocationException { - Long totalVolumes = 0L; - Long totalVolumesSize = 0L; - List disks = new ArrayList<>(); - disks.add(rootDisk); - disks.addAll(dataDisks); - Map diskOfferingMap = new HashMap<>(dataDiskOfferingMap); - diskOfferingMap.put(rootDisk.getDiskId(), serviceOffering.getDiskOfferingId()); - Map diskOfferingVolumeCountMap = new HashMap<>(); - Map diskOfferingSizeMap = new HashMap<>(); - for (UnmanagedInstanceTO.Disk disk : disks) { - totalVolumes++; - totalVolumesSize += disk.getCapacity(); - Long diskOfferingId = diskOfferingMap.get(disk.getDiskId()); - if (diskOfferingVolumeCountMap.containsKey(diskOfferingId)) { - diskOfferingVolumeCountMap.put(diskOfferingId, diskOfferingVolumeCountMap.get(diskOfferingId) + 1); - diskOfferingSizeMap.put(diskOfferingId, diskOfferingSizeMap.get(diskOfferingId) + disk.getCapacity()); - } else { - diskOfferingVolumeCountMap.put(diskOfferingId, 1L); - diskOfferingSizeMap.put(diskOfferingId, disk.getCapacity()); - } - } - resourceLimitService.checkResourceLimit(account, Resource.ResourceType.volume, totalVolumes); - resourceLimitService.checkResourceLimit(account, Resource.ResourceType.primary_storage, totalVolumesSize); - for (Long diskOfferingId : diskOfferingVolumeCountMap.keySet()) { - List tags = resourceLimitService.getResourceLimitStorageTags(diskOfferingDao.findById(diskOfferingId)); - for (String tag : tags) { - resourceLimitService.checkResourceLimitWithTag(account, Resource.ResourceType.volume, tag, diskOfferingVolumeCountMap.get(diskOfferingId)); - resourceLimitService.checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, tag, diskOfferingSizeMap.get(diskOfferingId)); - } - } - } - private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedInstance, final String instanceNameInternal, final DataCenter zone, final Cluster cluster, final HostVO host, final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId, final ServiceOfferingVO serviceOffering, final Map dataDiskOfferingMap, @@ -1164,17 +1136,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { allDetails.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(size)); } + List reservations = new ArrayList<>(); try { - checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), rootDisk, null, validatedServiceOffering, owner, zone, cluster, migrateAllowed); - if (CollectionUtils.isNotEmpty(dataDisks)) { // Data disk(s) present - checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), dataDisks, dataDiskOfferingMap, owner, zone, cluster, migrateAllowed); - allDetails.put(VmDetailConstants.DATA_DISK_CONTROLLER, dataDisks.get(0).getController()); - } - checkUnmanagedDiskLimits(owner, rootDisk, serviceOffering, dataDisks, dataDiskOfferingMap); - } catch (ResourceAllocationException e) { - logger.error("Volume resource allocation error for owner: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resource allocation error for owner: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), rootDisk, null, validatedServiceOffering, owner, zone, cluster, migrateAllowed, reservations); + if (CollectionUtils.isNotEmpty(dataDisks)) { // Data disk(s) present + checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), dataDisks, dataDiskOfferingMap, owner, zone, cluster, migrateAllowed, reservations); + allDetails.put(VmDetailConstants.DATA_DISK_CONTROLLER, dataDisks.get(0).getController()); } + // Check NICs and supplied networks Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, cluster.getHypervisorType()); @@ -1257,6 +1226,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } publishVMUsageUpdateResourceCount(userVm, validatedServiceOffering, template); return userVm; + + } catch (ResourceAllocationException e) { // This will be thrown by checkUnmanagedDiskAndOfferingForImport, so the VM was not imported yet + logger.error("Volume resource allocation error for owner: {}", owner, e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resource allocation error for owner: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + } finally { + ReservationHelper.closeAll(reservations); + } } private void addImportingVMBootTypeAndModeDetails(String bootType, String bootMode, Map allDetails) { @@ -1361,8 +1337,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { VMTemplateVO template = getTemplateForImportInstance(cmd.getTemplateId(), cluster.getHypervisorType()); ServiceOfferingVO serviceOffering = getServiceOfferingForImportInstance(cmd.getServiceOfferingId(), owner, zone); - checkResourceLimitForImportInstance(owner); - String displayName = getDisplayNameForImportInstance(cmd.getDisplayName(), instanceName); String hostName = getHostNameForImportInstance(cmd.getHostName(), cluster.getHypervisorType(), instanceName, displayName); @@ -1378,6 +1352,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List managedVms = new ArrayList<>(additionalNameFilters); managedVms.addAll(getHostsManagedVms(hosts)); + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + try (CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, Long.valueOf(serviceOffering.getRamSize()), reservationDao, resourceLimitService)) { + ActionEventUtils.onStartedActionEvent(userId, owner.getId(), EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, true, 0); @@ -1405,6 +1384,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } + } catch (ResourceAllocationException e) { + logger.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + } + if (userVm == null) { ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, 0); @@ -1464,15 +1448,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return StringUtils.isEmpty(displayName) ? instanceName : displayName; } - private void checkResourceLimitForImportInstance(Account owner) { - try { - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.user_vm, 1); - } catch (ResourceAllocationException e) { - logger.error("VM resource allocation error for account: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); - } - } - private ServiceOfferingVO getServiceOfferingForImportInstance(Long serviceOfferingId, Account owner, DataCenter zone) { if (serviceOfferingId == null) { throw new InvalidParameterValueException("Service offering ID cannot be null"); @@ -2338,12 +2313,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new InvalidParameterValueException(String.format("Service offering ID: %d cannot be found", serviceOfferingId)); } accountService.checkAccess(owner, serviceOffering, zone); - try { - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.user_vm, 1); - } catch (ResourceAllocationException e) { - logger.error("VM resource allocation error for account: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); - } String displayName = cmd.getDisplayName(); if (StringUtils.isEmpty(displayName)) { displayName = instanceName; @@ -2431,6 +2400,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { UserVm userVm = null; + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + try (CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, Long.valueOf(serviceOffering.getRamSize()), reservationDao, resourceLimitService)) { + if (ImportSource.EXTERNAL == importSource) { String username = cmd.getUsername(); String password = cmd.getPassword(); @@ -2451,6 +2425,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new RuntimeException(e); } } + + } catch (ResourceAllocationException e) { + logger.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + } + if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import Vm with name: %s ", instanceName)); } @@ -2464,7 +2444,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId, final ServiceOfferingVO serviceOffering, final Map dataDiskOfferingMap, final Map nicNetworkMap, final Map callerNicIpAddressMap, - final String remoteUrl, String username, String password, String tmpPath, final Map details) { + final String remoteUrl, String username, String password, String tmpPath, final Map details) throws ResourceAllocationException { UserVm userVm = null; Map allDetails = new HashMap<>(details); @@ -2475,6 +2455,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("No attached disks found for the unmanaged VM: %s", instanceName)); } + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); Pair> rootAndDataDisksPair = getRootAndDataDisks(unmanagedInstanceDisks, dataDiskOfferingMap); final UnmanagedInstanceTO.Disk rootDisk = rootAndDataDisksPair.first(); final List dataDisks = rootAndDataDisksPair.second(); @@ -2483,6 +2464,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } allDetails.put(VmDetailConstants.ROOT_DISK_CONTROLLER, rootDisk.getController()); + List reservations = new ArrayList<>(); + try { + checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, dataDisks, diskOffering, dataDiskOfferingMap, reservations); + // Check NICs and supplied networks Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, Hypervisor.HypervisorType.KVM); @@ -2503,16 +2488,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); } - DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); String rootVolumeName = String.format("ROOT-%s", userVm.getId()); DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null); DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()]; int diskSeq = 0; for (UnmanagedInstanceTO.Disk disk : dataDisks) { - if (disk.getCapacity() == null || disk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Disk ID: %s size is invalid", disk.getDiskId())); - } DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null); dataDiskProfiles[diskSeq++] = dataDiskProfile; @@ -2537,10 +2518,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List> diskProfileStoragePoolList = new ArrayList<>(); try { - if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); - } - diskProfileStoragePoolList.add(importExternalDisk(rootDisk, userVm, dest, diskOffering, Volume.Type.ROOT, template, null, remoteUrl, username, password, tmpPath, diskProfile)); @@ -2574,6 +2551,30 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; + + } finally { + ReservationHelper.closeAll(reservations); + } + } + + protected void checkVolumeResourceLimitsForExternalKvmVmImport(Account owner, UnmanagedInstanceTO.Disk rootDisk, + List dataDisks, DiskOfferingVO rootDiskOffering, + Map dataDiskOfferingMap, List reservations) throws ResourceAllocationException { + if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 0) { + throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); + } + resourceLimitService.checkVolumeResourceLimit(owner, true, rootDisk.getCapacity(), rootDiskOffering, reservations); + + if (CollectionUtils.isEmpty(dataDisks)) { + return; + } + for (UnmanagedInstanceTO.Disk disk : dataDisks) { + if (disk.getCapacity() == null || disk.getCapacity() == 0) { + throw new InvalidParameterValueException(String.format("Data disk ID: %s size is invalid", disk.getDiskId())); + } + DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); + resourceLimitService.checkVolumeResourceLimit(owner, true, disk.getCapacity(), offering, reservations); + } } private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, final String instanceName, final DataCenter zone, @@ -2641,7 +2642,16 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); } + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + + List reservations = new ArrayList<>(); + List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); + try { + CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L : 0L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + String rootVolumeName = String.format("ROOT-%s", userVm.getId()); DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null); @@ -2686,6 +2696,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new CloudRuntimeException("Disk not found or is invalid"); } diskProfile.setSize(checkVolumeAnswer.getSize()); + try { + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); + } catch (ResourceAllocationException e) { + cleanupFailedImportVM(userVm); + throw e; + } List> diskProfileStoragePoolList = new ArrayList<>(); try { @@ -2705,6 +2723,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; + + } finally { + ReservationHelper.closeAll(reservations); + } } private void checkVolume(Map volumeDetails) { diff --git a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java index e3bfdc63635..bdc6620c490 100644 --- a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java +++ b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.TaggedResourceLimitAndCountResponse; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -40,6 +41,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @@ -264,6 +266,7 @@ public class ResourceLimitManagerImplTest extends TestCase { @Test public void testCheckVmResourceLimit() { + List reservations = new ArrayList<>(); ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); Mockito.when(serviceOffering.getHostTag()).thenReturn(hostTags.get(0)); @@ -271,53 +274,12 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.when(serviceOffering.getRamSize()).thenReturn(256); Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0)); Account account = Mockito.mock(Account.class); - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVmResourceLimit(account, true, serviceOffering, template); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + resourceLimitManager.checkVmResourceLimit(account, true, serviceOffering, template, reservations); List tags = new ArrayList<>(); tags.add(null); tags.add(hostTags.get(0)); - for (String tag: tags) { - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.user_vm, tag); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, tag, 2L); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.memory, tag, 256L); - } - } catch (ResourceAllocationException e) { - Assert.fail("Exception encountered: " + e.getMessage()); - } - } - - @Test - public void testCheckVmCpuResourceLimit() { - ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); - VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); - Mockito.when(serviceOffering.getHostTag()).thenReturn(hostTags.get(0)); - Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0)); - Account account = Mockito.mock(Account.class); - long cpu = 2L; - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVmCpuResourceLimit(account, true, serviceOffering, template, cpu); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, null, cpu); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, hostTags.get(0), cpu); - } catch (ResourceAllocationException e) { - Assert.fail("Exception encountered: " + e.getMessage()); - } - } - - @Test - public void testCheckVmMemoryResourceLimit() { - ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); - VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); - Mockito.when(serviceOffering.getHostTag()).thenReturn(hostTags.get(0)); - Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0)); - Account account = Mockito.mock(Account.class); - long delta = 256L; - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVmMemoryResourceLimit(account, true, serviceOffering, template, delta); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.memory, null, delta); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.memory, hostTags.get(0), delta); + Assert.assertEquals(3, mockCheckedReservation.constructed().size()); } catch (ResourceAllocationException e) { Assert.fail("Exception encountered: " + e.getMessage()); } @@ -325,21 +287,15 @@ public class ResourceLimitManagerImplTest extends TestCase { @Test public void testCheckVolumeResourceLimit() { + List reservations = new ArrayList<>(); String checkTag = storageTags.get(0); DiskOffering diskOffering = Mockito.mock(DiskOffering.class); Mockito.when(diskOffering.getTags()).thenReturn(checkTag); Mockito.when(diskOffering.getTagsArray()).thenReturn(new String[]{checkTag}); Account account = Mockito.mock(Account.class); - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVolumeResourceLimit(account, true, 100L, diskOffering); - List tags = new ArrayList<>(); - tags.add(null); - tags.add(checkTag); - for (String tag: tags) { - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.volume, tag); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, tag, 100L); - } + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + resourceLimitManager.checkVolumeResourceLimit(account, true, 100L, diskOffering, reservations); + Assert.assertEquals(2, reservations.size()); } catch (ResourceAllocationException e) { Assert.fail("Exception encountered: " + e.getMessage()); } @@ -934,13 +890,6 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.anyList(), Mockito.eq(tag)); } - private void mockCheckResourceLimitWithTag() throws ResourceAllocationException { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag( - Mockito.any(Account.class), Mockito.any(Resource.ResourceType.class), Mockito.anyString()); - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag( - Mockito.any(Account.class), Mockito.any(Resource.ResourceType.class), Mockito.anyString(), Mockito.anyLong()); - } - private void mockIncrementResourceCountWithTag() { Mockito.doNothing().when(resourceLimitManager).incrementResourceCountWithTag( Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyString()); @@ -957,6 +906,7 @@ public class ResourceLimitManagerImplTest extends TestCase { @Test public void testCheckVolumeResourceCount() throws ResourceAllocationException { + List reservations = new ArrayList<>(); Account account = Mockito.mock(Account.class); String tag = "tag"; long delta = 10L; @@ -970,12 +920,11 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.doReturn(List.of(tag)).when(resourceLimitManager) .getResourceLimitStorageTagsForResourceCountOperation(Mockito.anyBoolean(), Mockito.any(DiskOffering.class)); - mockCheckResourceLimitWithTag(); - resourceLimitManager.checkVolumeResourceLimit(account, false, delta, Mockito.mock(DiskOffering.class)); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag( - account, Resource.ResourceType.volume, tag); - Mockito.verify(resourceLimitManager, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, tag, 10L); + + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + resourceLimitManager.checkVolumeResourceLimit(account, false, delta, Mockito.mock(DiskOffering.class), reservations); + Assert.assertEquals(2, reservations.size()); + } } @Test diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index 6c25e69876f..5a81d7d8ce3 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -26,7 +26,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -41,6 +40,7 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.user.volume.CheckAndRepairVolumeCmd; @@ -83,6 +83,7 @@ import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; @@ -91,7 +92,6 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.api.query.dao.ServiceOfferingJoinDao; import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; @@ -545,7 +545,9 @@ public class VolumeApiServiceImplTest { @Test public void attachRootVolumePositive() throws NoSuchFieldException, IllegalAccessException { thrown.expect(NullPointerException.class); - volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L, false); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L, false); + } } // Negative test - attach data volume, to the vm on non-kvm hypervisor @@ -564,7 +566,9 @@ public class VolumeApiServiceImplTest { DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class); when(diskOffering.getEncrypt()).thenReturn(true); when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering); - volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L, false); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L, false); + } } // volume not Ready @@ -649,9 +653,7 @@ public class VolumeApiServiceImplTest { * The resource limit check for primary storage should not be skipped for Volume in 'Uploaded' state. */ @Test - public void testResourceLimitCheckForUploadedVolume() throws NoSuchFieldException, IllegalAccessException, ResourceAllocationException { - doThrow(new ResourceAllocationException("primary storage resource limit check failed", Resource.ResourceType.primary_storage)).when(resourceLimitServiceMock) - .checkResourceLimit(any(AccountVO.class), any(Resource.ResourceType.class), any(Long.class)); + public void testAttachVolumeToVMPerformsResourceReservation() throws NoSuchFieldException, IllegalAccessException, ResourceAllocationException { UserVmVO vm = Mockito.mock(UserVmVO.class); AccountVO acc = Mockito.mock(AccountVO.class); VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); @@ -672,10 +674,10 @@ public class VolumeApiServiceImplTest { DataCenterVO zoneWithDisabledLocalStorage = Mockito.mock(DataCenterVO.class); when(_dcDao.findById(anyLong())).thenReturn(zoneWithDisabledLocalStorage); when(zoneWithDisabledLocalStorage.isLocalStorageEnabled()).thenReturn(true); - try { + doReturn(volumeVoMock).when(volumeApiServiceImpl).getVolumeAttachJobResult(Mockito.any(), Mockito.any(), Mockito.any()); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { volumeApiServiceImpl.attachVolumeToVM(2L, 9L, null, false); - } catch (InvalidParameterValueException e) { - Assert.assertEquals(e.getMessage(), ("primary storage resource limit check failed")); + Assert.assertEquals(1, mockCheckedReservation.constructed().size()); } } @@ -2199,4 +2201,34 @@ public class VolumeApiServiceImplTest { Assert.fail(); } } + + @Test + public void getRequiredPrimaryStorageSizeForVolumeAttachTestTagsAreEmptyReturnsZero() { + List tags = new ArrayList<>(); + + Long result = volumeApiServiceImpl.getRequiredPrimaryStorageSizeForVolumeAttach(tags, volumeInfoMock); + + Assert.assertEquals(0L, (long) result); + } + + @Test + public void getRequiredPrimaryStorageSizeForVolumeAttachTestVolumeIsReadyReturnsZero() { + List tags = List.of("tag1", "tag2"); + Mockito.doReturn(Volume.State.Ready).when(volumeInfoMock).getState(); + + Long result = volumeApiServiceImpl.getRequiredPrimaryStorageSizeForVolumeAttach(tags, volumeInfoMock); + + Assert.assertEquals(0L, (long) result); + } + + @Test + public void getRequiredPrimaryStorageSizeForVolumeAttachTestTagsAreNotEmptyAndVolumeIsUploadedReturnsVolumeSize() { + List tags = List.of("tag1", "tag2"); + Mockito.doReturn(Volume.State.Uploaded).when(volumeInfoMock).getState(); + Mockito.doReturn(2L).when(volumeInfoMock).getSize(); + + Long result = volumeApiServiceImpl.getRequiredPrimaryStorageSizeForVolumeAttach(tags, volumeInfoMock); + + Assert.assertEquals(2L, (long) result); + } } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 06fb65921c3..3ef304f4ec7 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import com.cloud.network.NetworkService; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; @@ -54,6 +55,7 @@ import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.template.VnfTemplateManager; @@ -65,6 +67,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @@ -609,7 +612,6 @@ public class UserVmManagerImplTest { Mockito.doNothing().when(userVmManagerImpl).validateOldAndNewAccounts(Mockito.nullable(Account.class), Mockito.nullable(Account.class), Mockito.anyLong(), Mockito.nullable(String.class), Mockito.nullable(Long.class)); Mockito.doNothing().when(userVmManagerImpl).validateIfVmHasNoRules(Mockito.any(), Mockito.anyLong()); Mockito.doNothing().when(userVmManagerImpl).removeInstanceFromInstanceGroup(Mockito.anyLong()); - Mockito.doNothing().when(userVmManagerImpl).verifyResourceLimitsForAccountAndStorage(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyList(), Mockito.any()); Mockito.doNothing().when(userVmManagerImpl).validateIfNewOwnerHasAccessToTemplate(Mockito.any(), Mockito.any(), Mockito.any()); Mockito.doNothing().when(userVmManagerImpl).updateVmOwner(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); @@ -1615,23 +1617,11 @@ public class UserVmManagerImplTest { Mockito.when(vol5.isDisplay()).thenReturn(true); List volumes = List.of(vol1, undisplayedVolume, vol3, vol4, vol5); - Long size = volumes.stream().filter(VolumeVO::isDisplay).mapToLong(VolumeVO::getSize).sum(); - try { - userVmManagerImpl.checkVolumesLimits(account, volumes); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimit(account, Resource.ResourceType.volume, 4); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimit(account, Resource.ResourceType.primary_storage, size); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.volume, "tag1", 2); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.volume, "tag2", 3); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, "tag1", - vol1.getSize() + vol5.getSize()); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, "tag2", - vol1.getSize() + vol3.getSize() + vol5.getSize()); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + userVmManagerImpl.checkVolumesLimits(account, volumes, reservations); + Assert.assertEquals(8, reservations.size()); } catch (ResourceAllocationException e) { Assert.fail(e.getMessage()); } @@ -1922,26 +1912,26 @@ public class UserVmManagerImplTest { @Test public void verifyResourceLimitsForAccountAndStorageTestCountOnlyRunningVmsInResourceLimitationIsTrueDoesNotCallVmResourceLimitCheck() throws ResourceAllocationException { + List reservations = new ArrayList<>(); LinkedList volumeVoList = new LinkedList(); Mockito.doReturn(true).when(userVmManagerImpl).countOnlyRunningVmsInResourceLimitation(); - userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock); + userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock, reservations); - Mockito.verify(resourceLimitMgr, Mockito.never()).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any()); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.volume, 0l); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.primary_storage, 0l); + Mockito.verify(resourceLimitMgr, Mockito.never()).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.verify(resourceLimitMgr, Mockito.never()).checkVolumeResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void verifyResourceLimitsForAccountAndStorageTestCountOnlyRunningVmsInResourceLimitationIsFalseCallsVmResourceLimitCheck() throws ResourceAllocationException { + List reservations = new ArrayList<>(); LinkedList volumeVoList = new LinkedList(); Mockito.doReturn(false).when(userVmManagerImpl).countOnlyRunningVmsInResourceLimitation(); - userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock); + userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock, reservations); - Mockito.verify(resourceLimitMgr).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any()); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.volume, 0l); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.primary_storage, 0l); + Mockito.verify(resourceLimitMgr).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.verify(userVmManagerImpl).checkVolumesLimits(Mockito.any(), Mockito.any(), Mockito.any()); } @Test @@ -2986,7 +2976,7 @@ public class UserVmManagerImplTest { configureDoNothingForMethodsThatWeDoNotWantToTest(); Mockito.doThrow(ResourceAllocationException.class).when(userVmManagerImpl).verifyResourceLimitsForAccountAndStorage(Mockito.any(), Mockito.any(), - Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); Assert.assertThrows(ResourceAllocationException.class, () -> userVmManagerImpl.moveVmToUser(assignVmCmdMock)); } diff --git a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java index 8d48fc4dac5..01512b448b2 100644 --- a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java +++ b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -357,13 +357,13 @@ public class VMSnapshotManagerTest { _vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO); verify(_vmSnapshotMgr).changeUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr).getVmMapDetails(userVm); + verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); } @Test public void testGetVmMapDetails() { - Map result = _vmSnapshotMgr.getVmMapDetails(userVm); + Map result = _vmSnapshotMgr.getVmMapDetails(vmSnapshotVO); assert(result.containsKey(userVmDetailCpuNumber.getName())); assert(result.containsKey(userVmDetailMemory.getName())); assertEquals(userVmDetails.size(), result.size()); @@ -375,7 +375,7 @@ public class VMSnapshotManagerTest { public void testChangeUserVmServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException { when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true); _vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr).getVmMapDetails(userVm); + verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); } @@ -383,7 +383,7 @@ public class VMSnapshotManagerTest { public void testChangeUserVmServiceOfferingFailOnUpgradeVMServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException { when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(false); _vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr).getVmMapDetails(userVm); + verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); } diff --git a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java index 045f2178529..2ac4d6c862c 100644 --- a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -24,6 +24,7 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.resourcelimit.Reserver; import org.springframework.stereotype.Component; import com.cloud.configuration.Resource.ResourceType; @@ -273,7 +274,7 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } @Override - public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException { + public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException { } @@ -284,13 +285,13 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc @Override public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException { + DiskOffering currentOffering, DiskOffering newOffering, List reservations) throws ResourceAllocationException { } @Override public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, - DiskOffering diskOffering) { + DiskOffering diskOffering, List reservations) { } @@ -334,7 +335,7 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } @Override - public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException { + public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { } @@ -351,19 +352,14 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc @Override public void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, - VirtualMachineTemplate template) throws ResourceAllocationException { + VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { } @Override public void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, VirtualMachineTemplate currentTemplate, - VirtualMachineTemplate newTemplate) throws ResourceAllocationException { - - } - - @Override - public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException { + VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException { } @@ -377,11 +373,6 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } - @Override - public void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException { - - } - @Override public void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) { diff --git a/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java index 419acc0ca0b..f3947a75e6e 100644 --- a/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java @@ -268,9 +268,6 @@ public class VolumeImportUnmanageManagerImplTest { doNothing().when(volumeImportUnmanageManager).checkIfVolumeIsEncrypted(volumeOnStorageTO); doNothing().when(volumeImportUnmanageManager).checkIfVolumeHasBackingFile(volumeOnStorageTO); - doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.volume); - doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.primary_storage, virtualSize); - DiskOfferingVO diskOffering = mock(DiskOfferingVO.class); when(diskOffering.isCustomized()).thenReturn(true); doReturn(diskOffering).when(volumeImportUnmanageManager).getOrCreateDiskOffering(account, diskOfferingId, zoneId, isLocal); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 09f62f7a049..282f3fb3a70 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.UUID; import com.cloud.offering.DiskOffering; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.ServerApiException; @@ -67,6 +68,7 @@ import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -89,7 +91,6 @@ import com.cloud.agent.api.GetUnmanagedInstancesCommand; import com.cloud.agent.api.ImportConvertedInstanceAnswer; import com.cloud.agent.api.ImportConvertedInstanceCommand; import com.cloud.agent.api.to.DataStoreTO; -import com.cloud.configuration.Resource; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; @@ -106,7 +107,6 @@ import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; -import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.UnsupportedServiceException; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -282,7 +282,6 @@ public class UnmanagedVMsManagerImplTest { clusterVO.setHypervisorType(Hypervisor.HypervisorType.VMware.toString()); when(clusterDao.findById(anyLong())).thenReturn(clusterVO); when(configurationDao.getValue(Mockito.anyString())).thenReturn(null); - doNothing().when(resourceLimitService).checkResourceLimit(any(Account.class), any(Resource.ResourceType.class), anyLong()); List hosts = new ArrayList<>(); HostVO hostVO = Mockito.mock(HostVO.class); when(hostVO.isInMaintenanceStates()).thenReturn(false); @@ -422,7 +421,8 @@ public class UnmanagedVMsManagerImplTest { when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance"); when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null); when(volumeApiService.doesStoragePoolSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true); - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd); } } @@ -520,7 +520,8 @@ public class UnmanagedVMsManagerImplTest { CopyRemoteVolumeAnswer copyAnswer = Mockito.mock(CopyRemoteVolumeAnswer.class); when(copyAnswer.getResult()).thenReturn(true); when(agentManager.easySend(anyLong(), any(CopyRemoteVolumeCommand.class))).thenReturn(copyAnswer); - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importVm(cmd); } } @@ -707,7 +708,8 @@ public class UnmanagedVMsManagerImplTest { Mockito.lenient().when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ImportConvertedInstanceCommand.class))).thenReturn(convertImportedInstanceAnswer); } - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importVm(importVmCmd); verify(vmwareGuru).getHypervisorVMOutOfBandAndCloneIfRequired(Mockito.eq(host), Mockito.eq(vmName), anyMap()); verify(vmwareGuru).createVMTemplateOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class), anyInt()); @@ -760,7 +762,8 @@ public class UnmanagedVMsManagerImplTest { when(volumeApiService.doesStoragePoolSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true); StoragePoolHostVO storagePoolHost = Mockito.mock(StoragePoolHostVO.class); when(storagePoolHostDao.findByPoolHost(anyLong(), anyLong())).thenReturn(storagePoolHost); - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importVm(cmd); } } @@ -1107,40 +1110,4 @@ public class UnmanagedVMsManagerImplTest { unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId); } - - @Test - public void testCheckUnmanagedDiskLimits() { - Account owner = Mockito.mock(Account.class); - UnmanagedInstanceTO.Disk disk = Mockito.mock(UnmanagedInstanceTO.Disk.class); - Mockito.when(disk.getDiskId()).thenReturn("disk1"); - Mockito.when(disk.getCapacity()).thenReturn(100L); - ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); - Mockito.when(serviceOffering.getDiskOfferingId()).thenReturn(1L); - UnmanagedInstanceTO.Disk dataDisk = Mockito.mock(UnmanagedInstanceTO.Disk.class); - Mockito.when(dataDisk.getDiskId()).thenReturn("disk2"); - Mockito.when(dataDisk.getCapacity()).thenReturn(1000L); - Map dataDiskMap = new HashMap<>(); - dataDiskMap.put("disk2", 2L); - DiskOfferingVO offering1 = Mockito.mock(DiskOfferingVO.class); - Mockito.when(diskOfferingDao.findById(1L)).thenReturn(offering1); - String tag1 = "tag1"; - Mockito.when(resourceLimitService.getResourceLimitStorageTags(offering1)).thenReturn(List.of(tag1)); - DiskOfferingVO offering2 = Mockito.mock(DiskOfferingVO.class); - Mockito.when(diskOfferingDao.findById(2L)).thenReturn(offering2); - String tag2 = "tag2"; - Mockito.when(resourceLimitService.getResourceLimitStorageTags(offering2)).thenReturn(List.of(tag2)); - try { - Mockito.doNothing().when(resourceLimitService).checkResourceLimit(any(), any(), any()); - Mockito.doNothing().when(resourceLimitService).checkResourceLimitWithTag(any(), any(), any(), any()); - unmanagedVMsManager.checkUnmanagedDiskLimits(owner, disk, serviceOffering, List.of(dataDisk), dataDiskMap); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimit(owner, Resource.ResourceType.volume, 2); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimit(owner, Resource.ResourceType.primary_storage, 1100L); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.volume, tag1,1); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.volume, tag2,1); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.primary_storage, tag1,100L); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.primary_storage, tag2,1000L); - } catch (ResourceAllocationException e) { - Assert.fail("Exception encountered: " + e.getMessage()); - } - } }