Fix resource count discrepancies (#8302)

* Fix resource count discrepancies

* Fixup while removing vm

* Fix discrepancies when starting VMs

* Fixup tests

* Fix failing tests

* Don't take lock when amount is negative

---------

Co-authored-by: dahn <daan@onecht.net>
This commit is contained in:
Vishesh 2024-03-13 18:22:44 +05:30 committed by GitHub
parent 6dc3d06037
commit e87c6cfcb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 398 additions and 154 deletions

View File

@ -34,6 +34,8 @@ ResourceReservation extends InternalIdentity {
Resource.ResourceType getResourceType();
Long getResourceId();
String getTag();
Long getReservedAmount();

View File

@ -1,4 +1,4 @@
// Licensed to the Apacohe Software Foundation (ASF) under one
// 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
@ -49,6 +49,7 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import com.cloud.configuration.Resource;
import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao;
import com.cloud.network.vpc.VpcVO;
@ -87,6 +88,7 @@ import org.apache.cloudstack.framework.messagebus.MessageDispatcher;
import org.apache.cloudstack.framework.messagebus.MessageHandler;
import org.apache.cloudstack.jobs.JobInfo;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
@ -296,6 +298,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@Inject
private VMInstanceDao _vmDao;
@Inject
private ReservationDao _reservationDao;
@Inject
private ServiceOfferingDao _offeringDao;
@Inject
private DiskOfferingDao _diskOfferingDao;
@ -914,7 +918,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@DB
protected Ternary<VMInstanceVO, ReservationContext, ItWorkVO> changeToStartState(final VirtualMachineGuru vmGuru, final VMInstanceVO vm, final User caller,
final Account account) throws ConcurrentOperationException {
final Account account, Account owner, ServiceOfferingVO offering, VirtualMachineTemplate template) throws ConcurrentOperationException {
final long vmId = vm.getId();
ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getType(), vm.getId());
@ -934,6 +938,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (logger.isDebugEnabled()) {
logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId());
}
if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
_resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template);
}
return new Ternary<>(vm, context, work);
}
@ -1126,7 +1133,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final VirtualMachineGuru vmGuru = getVmGuru(vm);
final Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = changeToStartState(vmGuru, vm, caller, account);
final Account owner = _entityMgr.findById(Account.class, vm.getAccountId());
final ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
final VirtualMachineTemplate template = _entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vm.getTemplateId());
final Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = changeToStartState(vmGuru, vm, caller, account, owner, offering, template);
if (start == null) {
return;
}
@ -1136,8 +1146,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
ItWorkVO work = start.third();
VMInstanceVO startedVm = null;
final ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
final VirtualMachineTemplate template = _entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vm.getTemplateId());
DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodIdToDeployIn(), null, null, null, null, ctx);
if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) {
@ -1150,12 +1158,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
// check resource count if ResourceCountRunningVMsonly.value() = true
final Account owner = _entityMgr.findById(Account.class, vm.getAccountId());
if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
_resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template);
}
boolean canRetry = true;
ExcludeList avoids = null;
try {
@ -2277,16 +2279,21 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
_workDao.update(work.getId(), work);
}
boolean result = Transaction.execute(new TransactionCallbackWithException<Boolean, NoTransitionException>() {
@Override
public Boolean doInTransaction(TransactionStatus status) throws NoTransitionException {
boolean result = stateTransitTo(vm, Event.OperationSucceeded, null);
if (result) {
vm.setPowerState(PowerState.PowerOff);
_vmDao.update(vm.getId(), vm);
if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
if (result && VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
_resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template);
}
} else {
return result;
}
});
if (!result) {
throw new CloudRuntimeException("unable to stop " + vm);
}
} catch (final NoTransitionException e) {
@ -2319,6 +2326,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
vm.setLastHostId(vm.getHostId());
}
}
if (e.equals(VirtualMachine.Event.DestroyRequested) || e.equals(VirtualMachine.Event.ExpungeOperation)) {
_reservationDao.setResourceId(Resource.ResourceType.user_vm, null);
_reservationDao.setResourceId(Resource.ResourceType.cpu, null);
_reservationDao.setResourceId(Resource.ResourceType.memory, null);
}
return _stateMachine.transitTo(vm, e, new Pair<>(vm.getHostId(), hostId), _vmDao);
}

View File

@ -1006,6 +1006,7 @@ public class VirtualMachineManagerImplTest {
public void testOrchestrateStartNonNullPodId() throws Exception {
VMInstanceVO vmInstance = new VMInstanceVO();
ReflectionTestUtils.setField(vmInstance, "id", 1L);
ReflectionTestUtils.setField(vmInstance, "accountId", 1L);
ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid");
ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L);
ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm");
@ -1019,6 +1020,7 @@ public class VirtualMachineManagerImplTest {
User user = mock(User.class);
Account account = mock(Account.class);
Account owner = mock(Account.class);
ReservationContext ctx = mock(ReservationContext.class);
@ -1042,12 +1044,13 @@ public class VirtualMachineManagerImplTest {
doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance);
Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = new Ternary<>(vmInstance, ctx, work);
Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account);
Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account, owner, serviceOffering, template);
when(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class));
when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering);
when(_entityMgr.findById(Account.class, vmInstance.getAccountId())).thenReturn(owner);
when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template);
Host destHost = mock(Host.class);
@ -1099,6 +1102,7 @@ public class VirtualMachineManagerImplTest {
public void testOrchestrateStartNullPodId() throws Exception {
VMInstanceVO vmInstance = new VMInstanceVO();
ReflectionTestUtils.setField(vmInstance, "id", 1L);
ReflectionTestUtils.setField(vmInstance, "accountId", 1L);
ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid");
ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L);
ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm");
@ -1112,6 +1116,7 @@ public class VirtualMachineManagerImplTest {
User user = mock(User.class);
Account account = mock(Account.class);
Account owner = mock(Account.class);
ReservationContext ctx = mock(ReservationContext.class);
@ -1135,12 +1140,13 @@ public class VirtualMachineManagerImplTest {
doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance);
Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = new Ternary<>(vmInstance, ctx, work);
Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account);
Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account, owner, serviceOffering, template);
when(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class));
when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering);
when(_entityMgr.findById(Account.class, vmInstance.getAccountId())).thenReturn(owner);
when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template);
Host destHost = mock(Host.class);

View File

@ -182,6 +182,7 @@ public class VolumeVO implements Volume {
@Column(name = "encrypt_format")
private String encryptFormat;
// Real Constructor
public VolumeVO(Type type, String name, long dcId, long domainId,
long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType, long size,

View File

@ -23,9 +23,15 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.configuration.Resource;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import org.apache.cloudstack.reservation.ReservationVO;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
@ -71,6 +77,8 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
protected GenericSearchBuilder<VolumeVO, SumCount> secondaryStorageSearch;
private final SearchBuilder<VolumeVO> poolAndPathSearch;
@Inject
ReservationDao reservationDao;
@Inject
ResourceTagDao _tagsDao;
protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?";
@ -443,6 +451,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountByAccount.and("displayVolume", CountByAccount.entity().isDisplayVolume(), Op.EQ);
CountByAccount.and("idNIN", CountByAccount.entity().getId(), Op.NIN);
CountByAccount.done();
primaryStorageSearch = createSearchBuilder(SumCount.class);
@ -454,6 +463,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
primaryStorageSearch.and("displayVolume", primaryStorageSearch.entity().isDisplayVolume(), Op.EQ);
primaryStorageSearch.and("isRemoved", primaryStorageSearch.entity().getRemoved(), Op.NULL);
primaryStorageSearch.and("NotCountStates", primaryStorageSearch.entity().getState(), Op.NIN);
primaryStorageSearch.and("idNIN", primaryStorageSearch.entity().getId(), Op.NIN);
primaryStorageSearch.done();
primaryStorageSearch2 = createSearchBuilder(SumCount.class);
@ -468,6 +478,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
primaryStorageSearch2.and("displayVolume", primaryStorageSearch2.entity().isDisplayVolume(), Op.EQ);
primaryStorageSearch2.and("isRemoved", primaryStorageSearch2.entity().getRemoved(), Op.NULL);
primaryStorageSearch2.and("NotCountStates", primaryStorageSearch2.entity().getState(), Op.NIN);
primaryStorageSearch2.and("idNIN", primaryStorageSearch2.entity().getId(), Op.NIN);
primaryStorageSearch2.done();
secondaryStorageSearch = createSearchBuilder(SumCount.class);
@ -506,15 +517,24 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
@Override
public Long countAllocatedVolumesForAccount(long accountId) {
List<ReservationVO> reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.volume, null);
List<Long> reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList());
SearchCriteria<Long> sc = CountByAccount.create();
sc.setParameters("account", accountId);
sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunged);
sc.setParameters("state", State.Destroy, State.Expunged);
sc.setParameters("displayVolume", 1);
if (CollectionUtils.isNotEmpty(reservedResourceIds)) {
sc.setParameters("idNIN", reservedResourceIds.toArray());
}
return customSearch(sc, null).get(0);
}
@Override
public long primaryStorageUsedForAccount(long accountId, List<Long> virtualRouters) {
List<ReservationVO> reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.volume, null);
List<Long> reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList());
SearchCriteria<SumCount> sc;
if (!virtualRouters.isEmpty()) {
sc = primaryStorageSearch2.create();
@ -526,6 +546,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
sc.setParameters("states", State.Allocated);
sc.setParameters("NotCountStates", State.Destroy, State.Expunged);
sc.setParameters("displayVolume", 1);
if (CollectionUtils.isNotEmpty(reservedResourceIds)) {
sc.setParameters("idNIN", reservedResourceIds.toArray());
}
List<SumCount> storageSpace = customSearch(sc, null);
if (storageSpace != null) {
return storageSpace.get(0).sum;
@ -863,4 +886,14 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
}
return listBy(sc);
}
@Override
public VolumeVO persist(VolumeVO entity) {
return Transaction.execute((TransactionCallback<VolumeVO>) status -> {
VolumeVO volume = super.persist(entity);
reservationDao.setResourceId(Resource.ResourceType.volume, volume.getId());
reservationDao.setResourceId(Resource.ResourceType.primary_storage, volume.getId());
return volume;
});
}
}

View File

@ -26,10 +26,16 @@ import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import com.cloud.configuration.Resource;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import org.apache.cloudstack.reservation.ReservationVO;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.network.Network;
@ -91,6 +97,8 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
NetworkDao networkDao;
@Inject
NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
@Inject
ReservationDao reservationDao;
private static final String LIST_PODS_HAVING_VMS_FOR_ACCOUNT =
"SELECT pod_id FROM cloud.vm_instance WHERE data_center_id = ? AND account_id = ? AND pod_id IS NOT NULL AND (state = 'Running' OR state = 'Stopped') "
@ -198,6 +206,7 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
CountByAccount.and("type", CountByAccount.entity().getType(), SearchCriteria.Op.EQ);
CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountByAccount.and("displayVm", CountByAccount.entity().isDisplayVm(), SearchCriteria.Op.EQ);
CountByAccount.and("idNIN", CountByAccount.entity().getId(), SearchCriteria.Op.NIN);
CountByAccount.done();
CountActiveAccount = createSearchBuilder(Long.class);
@ -697,6 +706,9 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
@Override
public Long countAllocatedVMsForAccount(long accountId, boolean runningVMsonly) {
List<ReservationVO> reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.user_vm, null);
List<Long> reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList());
SearchCriteria<Long> sc = CountByAccount.create();
sc.setParameters("account", accountId);
sc.setParameters("type", VirtualMachine.Type.User);
@ -705,6 +717,11 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
else
sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging});
sc.setParameters("displayVm", 1);
if (CollectionUtils.isNotEmpty(reservedResourceIds)) {
sc.setParameters("idNIN", reservedResourceIds.toArray());
}
return customSearch(sc, null).get(0);
}
@ -792,4 +809,15 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
sc.setParameters("ids", ids.toArray());
return listBy(sc);
}
@Override
public UserVmVO persist(UserVmVO entity) {
return Transaction.execute((TransactionCallback<UserVmVO>) status -> {
UserVmVO userVM = super.persist(entity);
reservationDao.setResourceId(Resource.ResourceType.user_vm, userVM.getId());
reservationDao.setResourceId(Resource.ResourceType.cpu, userVM.getId());
reservationDao.setResourceId(Resource.ResourceType.memory, userVM.getId());
return userVM;
});
}
}

View File

@ -51,6 +51,9 @@ public class ReservationVO implements ResourceReservation {
@Column(name = "tag")
String tag;
@Column(name = "resource_id")
Long resourceId;
@Column(name = "amount")
long amount;
@ -58,8 +61,8 @@ public class ReservationVO implements ResourceReservation {
}
public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, String tag, Long delta) {
if (delta == null || delta <= 0) {
throw new CloudRuntimeException("resource reservations can not be made for no resources");
if (delta == null) {
throw new CloudRuntimeException("resource reservations can not be made for null resources");
}
this.accountId = accountId;
this.domainId = domainId;
@ -101,4 +104,14 @@ public class ReservationVO implements ResourceReservation {
public Long getReservedAmount() {
return amount;
}
@Override
public Long getResourceId() {
return resourceId;
}
public void setResourceId(long resourceId) {
this.resourceId = resourceId;
}
}

View File

@ -23,7 +23,12 @@ import org.apache.cloudstack.reservation.ReservationVO;
import com.cloud.configuration.Resource;
import com.cloud.utils.db.GenericDao;
import java.util.List;
public interface ReservationDao extends GenericDao<ReservationVO, Long> {
long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag);
long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag);
void setResourceId(Resource.ResourceType type, Long resourceId);
List<Long> getResourceIds(long accountId, Resource.ResourceType type);
List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag);
}

View File

@ -19,27 +19,51 @@
package org.apache.cloudstack.reservation.dao;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.reservation.ReservationVO;
import com.cloud.configuration.Resource;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import org.apache.cloudstack.user.ResourceReservation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> implements ReservationDao {
protected transient Logger logger = LogManager.getLogger(getClass());
private static final String RESOURCE_TYPE = "resourceType";
private static final String RESOURCE_TAG = "resourceTag";
private static final String RESOURCE_ID = "resourceId";
private static final String ACCOUNT_ID = "accountId";
private static final String DOMAIN_ID = "domainId";
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeSearch;
private final SearchBuilder<ReservationVO> listAccountAndTypeSearch;
private final SearchBuilder<ReservationVO> listAccountAndTypeAndNoTagSearch;
private final SearchBuilder<ReservationVO> listDomainAndTypeSearch;
private final SearchBuilder<ReservationVO> listDomainAndTypeAndNoTagSearch;
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeAndNoTagSearch;
public ReservationDaoImpl() {
listResourceByAccountAndTypeSearch = createSearchBuilder();
listResourceByAccountAndTypeSearch.and(ACCOUNT_ID, listResourceByAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
listResourceByAccountAndTypeSearch.and(RESOURCE_TYPE, listResourceByAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
listResourceByAccountAndTypeSearch.and(RESOURCE_ID, listResourceByAccountAndTypeSearch.entity().getResourceId(), SearchCriteria.Op.NNULL);
listResourceByAccountAndTypeSearch.and(RESOURCE_TAG, listResourceByAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ);
listResourceByAccountAndTypeSearch.done();
listResourceByAccountAndTypeAndNoTagSearch = createSearchBuilder();
listResourceByAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listResourceByAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listResourceByAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_ID, listResourceByAccountAndTypeAndNoTagSearch.entity().getResourceId(), SearchCriteria.Op.NNULL);
listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listResourceByAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL);
listResourceByAccountAndTypeAndNoTagSearch.done();
listAccountAndTypeSearch = createSearchBuilder();
listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
@ -98,4 +122,43 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
}
return total;
}
@Override
public void setResourceId(Resource.ResourceType type, Long resourceId) {
Object obj = CallContext.current().getContextParameter(String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName()));
if (obj instanceof List) {
try {
List<Long> reservationIds = (List<Long>)obj;
for (Long reservationId : reservationIds) {
ReservationVO reservation = findById(reservationId);
if (reservation != null) {
reservation.setResourceId(resourceId);
persist(reservation);
}
}
} catch (Exception e) {
logger.warn("Failed to persist reservation for resource type " + type.getName() + " for resource id " + resourceId, e);
}
}
}
@Override
public List<Long> getResourceIds(long accountId, Resource.ResourceType type) {
SearchCriteria<ReservationVO> sc = listResourceByAccountAndTypeSearch.create();
sc.setParameters(ACCOUNT_ID, accountId);
sc.setParameters(RESOURCE_TYPE, type);
return listBy(sc).stream().map(ReservationVO::getResourceId).collect(Collectors.toList());
}
@Override
public List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) {
SearchCriteria<ReservationVO> sc = tag == null ?
listResourceByAccountAndTypeAndNoTagSearch.create() : listResourceByAccountAndTypeSearch.create();
sc.setParameters(ACCOUNT_ID, accountId);
sc.setParameters(RESOURCE_TYPE, type);
if (tag != null) {
sc.setParameters(RESOURCE_TAG, tag);
}
return listBy(sc);
}
}

View File

@ -29,6 +29,14 @@ DROP INDEX `i_resource_count__type_domaintId`,
ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`),
ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`);
ALTER TABLE `cloud`.`resource_reservation`
ADD COLUMN `resource_id` bigint unsigned NULL;
ALTER TABLE `cloud`.`resource_reservation`
MODIFY COLUMN `amount` bigint NOT NULL;
-- Update Default System offering for Router to 512MiB
UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local",
"Cloud.Com-InternalLBVm", "Cloud.Com-InternalLBVm-Local",
@ -61,4 +69,3 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_nsx', 'int(1
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"');

View File

@ -955,5 +955,4 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
public String getUserDataDetails() {
return userDataDetails;
}
}

View File

@ -62,13 +62,32 @@ public class CheckedReservation implements AutoCloseable {
return String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName());
}
protected void checkLimitAndPersistReservation(Account account, ResourceType resourceType, String tag, Long amount) throws ResourceAllocationException {
protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount) throws ResourceAllocationException {
checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount);
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
for (String tag : resourceLimitTags) {
checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount);
}
}
}
protected void checkLimitAndPersistReservation(Account account, ResourceType resourceType, Long resourceId, String tag, Long amount) throws ResourceAllocationException {
if (amount > 0) {
resourceLimitService.checkResourceLimitWithTag(account, resourceType, tag, amount);
}
ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), resourceType, tag, amount);
if (resourceId != null) {
reservationVO.setResourceId(resourceId);
}
ResourceReservation reservation = reservationDao.persist(reservationVO);
this.reservations.add(reservation);
}
public CheckedReservation(Account account, ResourceType resourceType, List<String> resourceLimitTags, Long amount,
ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, resourceType, null, resourceLimitTags, amount, reservationDao, resourceLimitService);
}
/**
* - check if adding a reservation is allowed
* - create DB entry for reservation
@ -77,7 +96,7 @@ public class CheckedReservation implements AutoCloseable {
* @param amount positive number of the resource type to reserve
* @throws ResourceAllocationException
*/
public CheckedReservation(Account account, ResourceType resourceType, List<String> resourceLimitTags, Long amount,
public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount,
ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this.reservationDao = reservationDao;
this.resourceLimitService = resourceLimitService;
@ -86,23 +105,13 @@ public class CheckedReservation implements AutoCloseable {
this.amount = amount;
this.reservations = new ArrayList<>();
this.resourceLimitTags = resourceLimitTags;
setGlobalLock();
if (this.amount != null && this.amount <= 0) {
if(logger.isDebugEnabled()){
logger.debug(String.format("not reserving no amount of resources for %s in domain %d, type: %s, %s ", account.getAccountName(), account.getDomainId(), resourceType, amount));
}
this.amount = null;
}
if (this.amount != null) {
if (this.amount != null && this.amount != 0) {
if (amount > 0) {
setGlobalLock();
if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) {
try {
checkLimitAndPersistReservation(account, resourceType, null, amount);
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
for (String tag: resourceLimitTags) {
checkLimitAndPersistReservation(account, resourceType, tag, amount);
}
}
checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount);
CallContext.current().putContextParameter(getContextParameterKey(), getIds());
} catch (NullPointerException npe) {
throw new CloudRuntimeException("not enough means to check limits", npe);
@ -113,9 +122,11 @@ public class CheckedReservation implements AutoCloseable {
throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType);
}
} else {
if(logger.isDebugEnabled()) {
logger.debug(String.format("not reserving no amount of resources for %s in domain %d, type: %s, tag: %s", account.getAccountName(), account.getDomainId(), resourceType, getResourceLimitTagsAsString()));
checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount);
}
} else {
logger.debug("not reserving any amount of resources for {} in domain {}, type: {}, tag: {}",
account.getAccountName(), account.getDomainId(), resourceType, getResourceLimitTagsAsString());
}
}

View File

@ -45,6 +45,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
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.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
@ -1273,9 +1274,11 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
if (CollectionUtils.isEmpty(offerings) && CollectionUtils.isEmpty(templates)) {
return new ArrayList<>();
}
return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states,
offerings.stream().map(ServiceOfferingVO::getId).collect(Collectors.toList()),
templates.stream().map(VMTemplateVO::getId).collect(Collectors.toList()));
templates.stream().map(VMTemplateVO::getId).collect(Collectors.toList())
);
}
protected List<UserVmJoinVO> getVmsWithAccount(long accountId) {
@ -1293,12 +1296,26 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
vrIds);
}
private long calculateReservedResources(List<UserVmJoinVO> vms, long accountId, ResourceType type, String tag) {
Set<Long> vmIds = vms.stream().map(UserVmJoinVO::getId).collect(Collectors.toSet());
List<ReservationVO> reservations = reservationDao.getReservationsForAccount(accountId, type, tag);
long reserved = 0;
for (ReservationVO reservation : reservations) {
if (vmIds.contains(reservation.getResourceId()) ? reservation.getReservedAmount() > 0 : reservation.getReservedAmount() < 0) {
reserved += reservation.getReservedAmount();
}
}
return reserved;
}
protected long calculateVmCountForAccount(long accountId, String tag) {
if (StringUtils.isEmpty(tag)) {
return _userVmDao.countAllocatedVMsForAccount(accountId, VirtualMachineManager.ResourceCountRunningVMsonly.value());
}
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
return vms.size();
long reservedVMs = calculateReservedResources(vms, accountId, ResourceType.user_vm, tag);
return vms.size() - reservedVMs;
}
protected long calculateVolumeCountForAccount(long accountId, String tag) {
@ -1316,10 +1333,12 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
long cputotal = 0;
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
for (UserVmJoinVO vm : vms) {
cputotal += vm.getCpu();
}
return cputotal;
long reservedCpus = calculateReservedResources(vms, accountId, ResourceType.cpu, tag);
return cputotal - reservedCpus;
}
protected long calculateVmMemoryCountForAccount(long accountId, String tag) {
@ -1328,10 +1347,12 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
long memory = 0;
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
for (UserVmJoinVO vm : vms) {
memory += vm.getRamSize();
}
return memory;
long reservedMemory = calculateReservedResources(vms, accountId, ResourceType.memory, tag);
return memory - reservedMemory;
}
public long countCpusForAccount(long accountId) {
@ -1340,7 +1361,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
for (UserVmJoinVO vm : userVms) {
cputotal += vm.getCpu();
}
return cputotal;
long reservedCpuTotal = calculateReservedResources(userVms, accountId, ResourceType.cpu, null);
return cputotal - reservedCpuTotal;
}
public long calculateMemoryForAccount(long accountId) {
@ -1349,7 +1371,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
for (UserVmJoinVO vm : userVms) {
ramtotal += vm.getRamSize();
}
return ramtotal;
long reservedRamTotal = calculateReservedResources(userVms, accountId, ResourceType.memory, null);
return ramtotal - reservedRamTotal;
}
public long calculateSecondaryStorageForAccount(long accountId) {
@ -1625,8 +1648,12 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
}
@DB
@Override
public void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
@ -1638,9 +1665,15 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
}
}
});
}
@DB
@Override
public void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
@ -1652,6 +1685,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
}
}
});
}
@Override
public void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) {
@ -1711,6 +1746,9 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
@Override
public void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
@ -1723,9 +1761,15 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
incrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram);
}
}
});
}
@Override
public void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) {
public void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering,
VirtualMachineTemplate template) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
@ -1738,6 +1782,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
decrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram);
}
}
});
}
@Override
public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException {

View File

@ -5700,6 +5700,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
boolean status;
State vmState = vm.getState();
Account owner = _accountMgr.getAccount(vm.getAccountId());
ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId());
try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, vmId, null, -1L, reservationDao, resourceLimitService);
CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, vmId, null, -1 * Long.valueOf(offering.getCpu()), reservationDao, resourceLimitService);
CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, vmId, null, -1 * Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService);
) {
try {
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
status = vmEntity.destroy(expunge);
@ -5721,7 +5729,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (vmState != State.Error) {
// Get serviceOffering and template for Virtual Machine
ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
//Update Resource Count for the given account
resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), offering, template);
@ -5732,6 +5739,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
ex.addProxyObject(vm.getUuid(), "vmId");
throw ex;
}
} catch (Exception e) {
throw new CloudRuntimeException("Failed to destroy vm with specified vmId", e);
}
}

View File

@ -19,7 +19,6 @@
package com.cloud.resourcelimit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
@ -108,9 +107,10 @@ public class CheckedReservationTest {
@Test
public void getNoAmount() {
Mockito.when(reservationDao.persist(Mockito.any())).thenReturn(reservation);
try (CheckedReservation cr = new CheckedReservation(account, Resource.ResourceType.cpu,-11l, reservationDao, resourceLimitService) ) {
Long amount = cr.getReservedAmount();
assertNull(amount);
assertEquals(Long.valueOf(-11L), amount);
} catch (NullPointerException npe) {
fail("NPE caught");
} catch (ResourceAllocationException rae) {

View File

@ -27,6 +27,7 @@ import org.apache.cloudstack.api.response.AccountResponse;
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.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@ -106,6 +107,8 @@ public class ResourceLimitManagerImplTest extends TestCase {
@Mock
ResourceCountDao resourceCountDao;
@Mock
private ReservationDao reservationDao;
@Mock
UserVmJoinDao userVmJoinDao;
@Mock
ServiceOfferingDao serviceOfferingDao;
@ -675,7 +678,9 @@ public class ResourceLimitManagerImplTest extends TestCase {
Assert.assertEquals(2L, resourceLimitManager.calculateVmCountForAccount(accountId, tag));
tag = "tag";
Mockito.doReturn(List.of(UserVmJoinVO.class)).when(resourceLimitManager).getVmsWithAccountAndTag(accountId, tag);
UserVmJoinVO vm = Mockito.mock(UserVmJoinVO.class);
Mockito.when(vm.getId()).thenReturn(1L);
Mockito.doReturn(List.of(vm)).when(resourceLimitManager).getVmsWithAccountAndTag(accountId, tag);
Assert.assertEquals(1L, resourceLimitManager.calculateVmCountForAccount(accountId, tag));
}

View File

@ -37,6 +37,7 @@ import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@ -123,10 +124,10 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
DomainVO domain = new DomainVO();
VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class);
when(_orchSrvc.getVirtualMachine(nullable(String.class))).thenReturn(vmEntity);
when(vmEntity.destroy(nullable(Boolean.class))).thenReturn(true);
lenient().when(_orchSrvc.getVirtualMachine(nullable(String.class))).thenReturn(vmEntity);
lenient().when(vmEntity.destroy(nullable(Boolean.class))).thenReturn(true);
Mockito.lenient().doReturn(vm).when(_vmDao).findById(nullable(Long.class));
lenient().doReturn(vm).when(_vmDao).findById(nullable(Long.class));
VolumeVO vol = new VolumeVO(VOLUME_UUID, 1l, 1l, 1l, 1l, 1l, "folder", "path", null, 50, Type.ROOT);
vol.setDisplayVolume(true);
@ -136,20 +137,20 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
lenient().when(securityChecker.checkAccess(Mockito.eq(account), nullable(ControlledEntity.class), nullable(AccessType.class), nullable(String.class))).thenReturn(true);
when(_userVmDao.findById(nullable(Long.class))).thenReturn(vm);
lenient().when(_userVmDao.findById(nullable(Long.class))).thenReturn(vm);
lenient().when(_userVmDao.listByAccountId(ACCOUNT_ID)).thenReturn(Arrays.asList(vm));
lenient().when(_userVmDao.findByUuid(nullable(String.class))).thenReturn(vm);
when(_volumeDao.findByInstance(nullable(Long.class))).thenReturn(volumes);
lenient().when(_volumeDao.findByInstance(nullable(Long.class))).thenReturn(volumes);
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
lenient().when(offering.getCpu()).thenReturn(500);
lenient().when(offering.getId()).thenReturn(1l);
when(serviceOfferingDao.findByIdIncludingRemoved(nullable(Long.class), nullable(Long.class))).thenReturn(offering);
lenient().when(serviceOfferingDao.findByIdIncludingRemoved(nullable(Long.class), nullable(Long.class))).thenReturn(offering);
lenient().when(_domainMgr.getDomain(nullable(Long.class))).thenReturn(domain);
Mockito.lenient().doReturn(true).when(_vmMgr).expunge(any(UserVmVO.class));
Mockito.doReturn(true).when(_vmMgr).expunge(any(UserVmVO.class));
}
@ -190,22 +191,22 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
// If the VM is already destroyed, no events should get emitted
public void destroyedVMRootVolumeUsageEvent()
throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException {
Mockito.lenient().doReturn(vm).when(_vmMgr).destroyVm(nullable(Long.class), nullable(Boolean.class));
lenient().doReturn(vm).when(_vmMgr).destroyVm(nullable(Long.class), nullable(Boolean.class));
List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(true);
Assert.assertEquals(0, emittedEvents.size());
}
@Ignore()
@Test
// If the VM is running, we should see one emitted event for the root
// volume.
public void runningVMRootVolumeUsageEvent()
throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException {
Mockito.doNothing().when(vmStatsDaoMock).removeAllByVmId(Mockito.anyLong());
Mockito.lenient().when(_vmMgr.destroyVm(nullable(Long.class), nullable(Boolean.class))).thenReturn(vm);
Mockito.when(_vmMgr.destroyVm(nullable(Long.class), nullable(Boolean.class))).thenReturn(vm);
List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(false);
UsageEventVO event = emittedEvents.get(0);
Assert.assertEquals(EventTypes.EVENT_VOLUME_DELETE, event.getType());
Assert.assertEquals(VOLUME_UUID, event.getResourceName());
}
}

View File

@ -75,4 +75,5 @@
<bean id="nsxControllerDaoImpl" class="com.cloud.network.dao.NsxProviderDaoImpl" />
<bean id="vlanDetailsDao" class="com.cloud.dc.dao.VlanDetailsDaoImpl" />
<bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
<bean id="reservationDao" class="org.apache.cloudstack.reservation.dao.ReservationDaoImpl" />
</beans>