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(); Resource.ResourceType getResourceType();
Long getResourceId();
String getTag(); String getTag();
Long getReservedAmount(); 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 // or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information // distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file // regarding copyright ownership. The ASF licenses this file
@ -49,6 +49,7 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException; import javax.persistence.EntityExistsException;
import com.cloud.configuration.Resource;
import com.cloud.domain.Domain; import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
import com.cloud.network.vpc.VpcVO; 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.framework.messagebus.MessageHandler;
import org.apache.cloudstack.jobs.JobInfo; import org.apache.cloudstack.jobs.JobInfo;
import org.apache.cloudstack.managed.context.ManagedContextRunnable; 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.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO;
@ -296,6 +298,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@Inject @Inject
private VMInstanceDao _vmDao; private VMInstanceDao _vmDao;
@Inject @Inject
private ReservationDao _reservationDao;
@Inject
private ServiceOfferingDao _offeringDao; private ServiceOfferingDao _offeringDao;
@Inject @Inject
private DiskOfferingDao _diskOfferingDao; private DiskOfferingDao _diskOfferingDao;
@ -914,7 +918,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@DB @DB
protected Ternary<VMInstanceVO, ReservationContext, ItWorkVO> changeToStartState(final VirtualMachineGuru vmGuru, final VMInstanceVO vm, final User caller, 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(); final long vmId = vm.getId();
ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getType(), 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()) { if (logger.isDebugEnabled()) {
logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); 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); return new Ternary<>(vm, context, work);
} }
@ -1126,7 +1133,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final VirtualMachineGuru vmGuru = getVmGuru(vm); 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) { if (start == null) {
return; return;
} }
@ -1136,8 +1146,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
ItWorkVO work = start.third(); ItWorkVO work = start.third();
VMInstanceVO startedVm = null; 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); DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodIdToDeployIn(), null, null, null, null, ctx);
if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) { if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) {
@ -1150,12 +1158,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); 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; boolean canRetry = true;
ExcludeList avoids = null; ExcludeList avoids = null;
try { try {
@ -2277,16 +2279,21 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
_workDao.update(work.getId(), work); _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); boolean result = stateTransitTo(vm, Event.OperationSucceeded, null);
if (result) {
vm.setPowerState(PowerState.PowerOff); if (result && VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
_vmDao.update(vm.getId(), vm);
if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
_resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template); _resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template);
} }
} else { return result;
}
});
if (!result) {
throw new CloudRuntimeException("unable to stop " + vm); throw new CloudRuntimeException("unable to stop " + vm);
} }
} catch (final NoTransitionException e) { } catch (final NoTransitionException e) {
@ -2319,6 +2326,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
vm.setLastHostId(vm.getHostId()); 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); 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 { public void testOrchestrateStartNonNullPodId() throws Exception {
VMInstanceVO vmInstance = new VMInstanceVO(); VMInstanceVO vmInstance = new VMInstanceVO();
ReflectionTestUtils.setField(vmInstance, "id", 1L); ReflectionTestUtils.setField(vmInstance, "id", 1L);
ReflectionTestUtils.setField(vmInstance, "accountId", 1L);
ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid"); ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid");
ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L); ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L);
ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm"); ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm");
@ -1019,6 +1020,7 @@ public class VirtualMachineManagerImplTest {
User user = mock(User.class); User user = mock(User.class);
Account account = mock(Account.class); Account account = mock(Account.class);
Account owner = mock(Account.class);
ReservationContext ctx = mock(ReservationContext.class); ReservationContext ctx = mock(ReservationContext.class);
@ -1042,12 +1044,13 @@ public class VirtualMachineManagerImplTest {
doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance); doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance);
Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = new Ternary<>(vmInstance, ctx, work); 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(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class));
when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering); 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); when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template);
Host destHost = mock(Host.class); Host destHost = mock(Host.class);
@ -1099,6 +1102,7 @@ public class VirtualMachineManagerImplTest {
public void testOrchestrateStartNullPodId() throws Exception { public void testOrchestrateStartNullPodId() throws Exception {
VMInstanceVO vmInstance = new VMInstanceVO(); VMInstanceVO vmInstance = new VMInstanceVO();
ReflectionTestUtils.setField(vmInstance, "id", 1L); ReflectionTestUtils.setField(vmInstance, "id", 1L);
ReflectionTestUtils.setField(vmInstance, "accountId", 1L);
ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid"); ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid");
ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L); ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L);
ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm"); ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm");
@ -1112,6 +1116,7 @@ public class VirtualMachineManagerImplTest {
User user = mock(User.class); User user = mock(User.class);
Account account = mock(Account.class); Account account = mock(Account.class);
Account owner = mock(Account.class);
ReservationContext ctx = mock(ReservationContext.class); ReservationContext ctx = mock(ReservationContext.class);
@ -1135,12 +1140,13 @@ public class VirtualMachineManagerImplTest {
doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance); doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance);
Ternary<VMInstanceVO, ReservationContext, ItWorkVO> start = new Ternary<>(vmInstance, ctx, work); 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(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class));
when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering); 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); when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template);
Host destHost = mock(Host.class); Host destHost = mock(Host.class);

View File

@ -182,6 +182,7 @@ public class VolumeVO implements Volume {
@Column(name = "encrypt_format") @Column(name = "encrypt_format")
private String encryptFormat; private String encryptFormat;
// Real Constructor // Real Constructor
public VolumeVO(Type type, String name, long dcId, long domainId, public VolumeVO(Type type, String name, long dcId, long domainId,
long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType, long size, 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.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject; 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.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -71,6 +77,8 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
protected GenericSearchBuilder<VolumeVO, SumCount> secondaryStorageSearch; protected GenericSearchBuilder<VolumeVO, SumCount> secondaryStorageSearch;
private final SearchBuilder<VolumeVO> poolAndPathSearch; private final SearchBuilder<VolumeVO> poolAndPathSearch;
@Inject @Inject
ReservationDao reservationDao;
@Inject
ResourceTagDao _tagsDao; ResourceTagDao _tagsDao;
protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?"; 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("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountByAccount.and("displayVolume", CountByAccount.entity().isDisplayVolume(), Op.EQ); CountByAccount.and("displayVolume", CountByAccount.entity().isDisplayVolume(), Op.EQ);
CountByAccount.and("idNIN", CountByAccount.entity().getId(), Op.NIN);
CountByAccount.done(); CountByAccount.done();
primaryStorageSearch = createSearchBuilder(SumCount.class); 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("displayVolume", primaryStorageSearch.entity().isDisplayVolume(), Op.EQ);
primaryStorageSearch.and("isRemoved", primaryStorageSearch.entity().getRemoved(), Op.NULL); primaryStorageSearch.and("isRemoved", primaryStorageSearch.entity().getRemoved(), Op.NULL);
primaryStorageSearch.and("NotCountStates", primaryStorageSearch.entity().getState(), Op.NIN); primaryStorageSearch.and("NotCountStates", primaryStorageSearch.entity().getState(), Op.NIN);
primaryStorageSearch.and("idNIN", primaryStorageSearch.entity().getId(), Op.NIN);
primaryStorageSearch.done(); primaryStorageSearch.done();
primaryStorageSearch2 = createSearchBuilder(SumCount.class); 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("displayVolume", primaryStorageSearch2.entity().isDisplayVolume(), Op.EQ);
primaryStorageSearch2.and("isRemoved", primaryStorageSearch2.entity().getRemoved(), Op.NULL); primaryStorageSearch2.and("isRemoved", primaryStorageSearch2.entity().getRemoved(), Op.NULL);
primaryStorageSearch2.and("NotCountStates", primaryStorageSearch2.entity().getState(), Op.NIN); primaryStorageSearch2.and("NotCountStates", primaryStorageSearch2.entity().getState(), Op.NIN);
primaryStorageSearch2.and("idNIN", primaryStorageSearch2.entity().getId(), Op.NIN);
primaryStorageSearch2.done(); primaryStorageSearch2.done();
secondaryStorageSearch = createSearchBuilder(SumCount.class); secondaryStorageSearch = createSearchBuilder(SumCount.class);
@ -506,15 +517,24 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
@Override @Override
public Long countAllocatedVolumesForAccount(long accountId) { 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(); SearchCriteria<Long> sc = CountByAccount.create();
sc.setParameters("account", accountId); sc.setParameters("account", accountId);
sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunged); sc.setParameters("state", State.Destroy, State.Expunged);
sc.setParameters("displayVolume", 1); sc.setParameters("displayVolume", 1);
if (CollectionUtils.isNotEmpty(reservedResourceIds)) {
sc.setParameters("idNIN", reservedResourceIds.toArray());
}
return customSearch(sc, null).get(0); return customSearch(sc, null).get(0);
} }
@Override @Override
public long primaryStorageUsedForAccount(long accountId, List<Long> virtualRouters) { 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; SearchCriteria<SumCount> sc;
if (!virtualRouters.isEmpty()) { if (!virtualRouters.isEmpty()) {
sc = primaryStorageSearch2.create(); sc = primaryStorageSearch2.create();
@ -526,6 +546,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
sc.setParameters("states", State.Allocated); sc.setParameters("states", State.Allocated);
sc.setParameters("NotCountStates", State.Destroy, State.Expunged); sc.setParameters("NotCountStates", State.Destroy, State.Expunged);
sc.setParameters("displayVolume", 1); sc.setParameters("displayVolume", 1);
if (CollectionUtils.isNotEmpty(reservedResourceIds)) {
sc.setParameters("idNIN", reservedResourceIds.toArray());
}
List<SumCount> storageSpace = customSearch(sc, null); List<SumCount> storageSpace = customSearch(sc, null);
if (storageSpace != null) { if (storageSpace != null) {
return storageSpace.get(0).sum; return storageSpace.get(0).sum;
@ -863,4 +886,14 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
} }
return listBy(sc); 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.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; 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.apache.commons.collections.CollectionUtils;
import com.cloud.network.Network; import com.cloud.network.Network;
@ -91,6 +97,8 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
NetworkDao networkDao; NetworkDao networkDao;
@Inject @Inject
NetworkOfferingServiceMapDao networkOfferingServiceMapDao; NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
@Inject
ReservationDao reservationDao;
private static final String LIST_PODS_HAVING_VMS_FOR_ACCOUNT = 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') " "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("type", CountByAccount.entity().getType(), SearchCriteria.Op.EQ);
CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountByAccount.and("displayVm", CountByAccount.entity().isDisplayVm(), SearchCriteria.Op.EQ); CountByAccount.and("displayVm", CountByAccount.entity().isDisplayVm(), SearchCriteria.Op.EQ);
CountByAccount.and("idNIN", CountByAccount.entity().getId(), SearchCriteria.Op.NIN);
CountByAccount.done(); CountByAccount.done();
CountActiveAccount = createSearchBuilder(Long.class); CountActiveAccount = createSearchBuilder(Long.class);
@ -697,6 +706,9 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
@Override @Override
public Long countAllocatedVMsForAccount(long accountId, boolean runningVMsonly) { 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(); SearchCriteria<Long> sc = CountByAccount.create();
sc.setParameters("account", accountId); sc.setParameters("account", accountId);
sc.setParameters("type", VirtualMachine.Type.User); sc.setParameters("type", VirtualMachine.Type.User);
@ -705,6 +717,11 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
else else
sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging});
sc.setParameters("displayVm", 1); sc.setParameters("displayVm", 1);
if (CollectionUtils.isNotEmpty(reservedResourceIds)) {
sc.setParameters("idNIN", reservedResourceIds.toArray());
}
return customSearch(sc, null).get(0); return customSearch(sc, null).get(0);
} }
@ -792,4 +809,15 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
sc.setParameters("ids", ids.toArray()); sc.setParameters("ids", ids.toArray());
return listBy(sc); 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") @Column(name = "tag")
String tag; String tag;
@Column(name = "resource_id")
Long resourceId;
@Column(name = "amount") @Column(name = "amount")
long 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) { public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, String tag, Long delta) {
if (delta == null || delta <= 0) { if (delta == null) {
throw new CloudRuntimeException("resource reservations can not be made for no resources"); throw new CloudRuntimeException("resource reservations can not be made for null resources");
} }
this.accountId = accountId; this.accountId = accountId;
this.domainId = domainId; this.domainId = domainId;
@ -101,4 +104,14 @@ public class ReservationVO implements ResourceReservation {
public Long getReservedAmount() { public Long getReservedAmount() {
return amount; 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.configuration.Resource;
import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDao;
import java.util.List;
public interface ReservationDao extends GenericDao<ReservationVO, Long> { public interface ReservationDao extends GenericDao<ReservationVO, Long> {
long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag); long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag);
long getDomainReservation(Long domain, 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; package org.apache.cloudstack.reservation.dao;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.ReservationVO;
import com.cloud.configuration.Resource; import com.cloud.configuration.Resource;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; 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 { 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_TYPE = "resourceType";
private static final String RESOURCE_TAG = "resourceTag"; 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 ACCOUNT_ID = "accountId";
private static final String DOMAIN_ID = "domainId"; private static final String DOMAIN_ID = "domainId";
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeSearch;
private final SearchBuilder<ReservationVO> listAccountAndTypeSearch; private final SearchBuilder<ReservationVO> listAccountAndTypeSearch;
private final SearchBuilder<ReservationVO> listAccountAndTypeAndNoTagSearch; private final SearchBuilder<ReservationVO> listAccountAndTypeAndNoTagSearch;
private final SearchBuilder<ReservationVO> listDomainAndTypeSearch; private final SearchBuilder<ReservationVO> listDomainAndTypeSearch;
private final SearchBuilder<ReservationVO> listDomainAndTypeAndNoTagSearch; private final SearchBuilder<ReservationVO> listDomainAndTypeAndNoTagSearch;
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeAndNoTagSearch;
public ReservationDaoImpl() { 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 = createSearchBuilder();
listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), 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; 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_accountId` (`type`,`tag`,`account_id`),
ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_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 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", 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", "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.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','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"'); 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() { public String getUserDataDetails() {
return userDataDetails; return userDataDetails;
} }
} }

View File

@ -62,13 +62,32 @@ public class CheckedReservation implements AutoCloseable {
return String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName()); 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); resourceLimitService.checkResourceLimitWithTag(account, resourceType, tag, amount);
}
ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), 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); ResourceReservation reservation = reservationDao.persist(reservationVO);
this.reservations.add(reservation); 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 * - check if adding a reservation is allowed
* - create DB entry for reservation * - create DB entry for reservation
@ -77,7 +96,7 @@ public class CheckedReservation implements AutoCloseable {
* @param amount positive number of the resource type to reserve * @param amount positive number of the resource type to reserve
* @throws ResourceAllocationException * @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 { ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this.reservationDao = reservationDao; this.reservationDao = reservationDao;
this.resourceLimitService = resourceLimitService; this.resourceLimitService = resourceLimitService;
@ -86,23 +105,13 @@ public class CheckedReservation implements AutoCloseable {
this.amount = amount; this.amount = amount;
this.reservations = new ArrayList<>(); this.reservations = new ArrayList<>();
this.resourceLimitTags = resourceLimitTags; 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(quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) { if (amount > 0) {
setGlobalLock();
if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) {
try { try {
checkLimitAndPersistReservation(account, resourceType, null, amount); checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount);
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
for (String tag: resourceLimitTags) {
checkLimitAndPersistReservation(account, resourceType, tag, amount);
}
}
CallContext.current().putContextParameter(getContextParameterKey(), getIds()); CallContext.current().putContextParameter(getContextParameterKey(), getIds());
} catch (NullPointerException npe) { } catch (NullPointerException npe) {
throw new CloudRuntimeException("not enough means to check limits", 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); throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType);
} }
} else { } else {
if(logger.isDebugEnabled()) { checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount);
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()));
} }
} 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.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.ReservationVO;
import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; 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)) { if (CollectionUtils.isEmpty(offerings) && CollectionUtils.isEmpty(templates)) {
return new ArrayList<>(); return new ArrayList<>();
} }
return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states, return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states,
offerings.stream().map(ServiceOfferingVO::getId).collect(Collectors.toList()), 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) { protected List<UserVmJoinVO> getVmsWithAccount(long accountId) {
@ -1293,12 +1296,26 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
vrIds); 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) { protected long calculateVmCountForAccount(long accountId, String tag) {
if (StringUtils.isEmpty(tag)) { if (StringUtils.isEmpty(tag)) {
return _userVmDao.countAllocatedVMsForAccount(accountId, VirtualMachineManager.ResourceCountRunningVMsonly.value()); return _userVmDao.countAllocatedVMsForAccount(accountId, VirtualMachineManager.ResourceCountRunningVMsonly.value());
} }
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag); 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) { protected long calculateVolumeCountForAccount(long accountId, String tag) {
@ -1316,10 +1333,12 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
} }
long cputotal = 0; long cputotal = 0;
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag); List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
for (UserVmJoinVO vm : vms) { for (UserVmJoinVO vm : vms) {
cputotal += vm.getCpu(); cputotal += vm.getCpu();
} }
return cputotal; long reservedCpus = calculateReservedResources(vms, accountId, ResourceType.cpu, tag);
return cputotal - reservedCpus;
} }
protected long calculateVmMemoryCountForAccount(long accountId, String tag) { protected long calculateVmMemoryCountForAccount(long accountId, String tag) {
@ -1328,10 +1347,12 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
} }
long memory = 0; long memory = 0;
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag); List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
for (UserVmJoinVO vm : vms) { for (UserVmJoinVO vm : vms) {
memory += vm.getRamSize(); memory += vm.getRamSize();
} }
return memory; long reservedMemory = calculateReservedResources(vms, accountId, ResourceType.memory, tag);
return memory - reservedMemory;
} }
public long countCpusForAccount(long accountId) { public long countCpusForAccount(long accountId) {
@ -1340,7 +1361,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
for (UserVmJoinVO vm : userVms) { for (UserVmJoinVO vm : userVms) {
cputotal += vm.getCpu(); cputotal += vm.getCpu();
} }
return cputotal; long reservedCpuTotal = calculateReservedResources(userVms, accountId, ResourceType.cpu, null);
return cputotal - reservedCpuTotal;
} }
public long calculateMemoryForAccount(long accountId) { public long calculateMemoryForAccount(long accountId) {
@ -1349,7 +1371,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
for (UserVmJoinVO vm : userVms) { for (UserVmJoinVO vm : userVms) {
ramtotal += vm.getRamSize(); ramtotal += vm.getRamSize();
} }
return ramtotal; long reservedRamTotal = calculateReservedResources(userVms, accountId, ResourceType.memory, null);
return ramtotal - reservedRamTotal;
} }
public long calculateSecondaryStorageForAccount(long accountId) { public long calculateSecondaryStorageForAccount(long accountId) {
@ -1625,8 +1648,12 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
} }
} }
@DB
@Override @Override
public void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) { 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); List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) { if (CollectionUtils.isEmpty(tags)) {
return; return;
@ -1638,9 +1665,15 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
} }
} }
} }
});
}
@DB
@Override @Override
public void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) { 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); List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) { if (CollectionUtils.isEmpty(tags)) {
return; return;
@ -1652,6 +1685,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
} }
} }
} }
});
}
@Override @Override
public void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) { public void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) {
@ -1711,6 +1746,9 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
@Override @Override
public void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) { 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); List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) { if (CollectionUtils.isEmpty(tags)) {
return; return;
@ -1723,9 +1761,15 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
incrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram); incrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram);
} }
} }
});
}
@Override @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); List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) { if (CollectionUtils.isEmpty(tags)) {
return; return;
@ -1738,6 +1782,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
decrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram); decrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram);
} }
} }
});
}
@Override @Override
public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException { 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; boolean status;
State vmState = vm.getState(); 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 { try {
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
status = vmEntity.destroy(expunge); status = vmEntity.destroy(expunge);
@ -5721,10 +5729,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (vmState != State.Error) { if (vmState != State.Error) {
// Get serviceOffering and template for Virtual Machine // Get serviceOffering and template for Virtual Machine
ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
//Update Resource Count for the given account //Update Resource Count for the given account
resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(),offering, template); resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), offering, template);
} }
return _vmDao.findById(vmId); return _vmDao.findById(vmId);
} else { } else {
@ -5732,6 +5739,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
ex.addProxyObject(vm.getUuid(), "vmId"); ex.addProxyObject(vm.getUuid(), "vmId");
throw ex; 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; package com.cloud.resourcelimit;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -108,9 +107,10 @@ public class CheckedReservationTest {
@Test @Test
public void getNoAmount() { public void getNoAmount() {
Mockito.when(reservationDao.persist(Mockito.any())).thenReturn(reservation);
try (CheckedReservation cr = new CheckedReservation(account, Resource.ResourceType.cpu,-11l, reservationDao, resourceLimitService) ) { try (CheckedReservation cr = new CheckedReservation(account, Resource.ResourceType.cpu,-11l, reservationDao, resourceLimitService) ) {
Long amount = cr.getReservedAmount(); Long amount = cr.getReservedAmount();
assertNull(amount); assertEquals(Long.valueOf(-11L), amount);
} catch (NullPointerException npe) { } catch (NullPointerException npe) {
fail("NPE caught"); fail("NPE caught");
} catch (ResourceAllocationException rae) { } 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.DomainResponse;
import org.apache.cloudstack.api.response.TaggedResourceLimitAndCountResponse; import org.apache.cloudstack.api.response.TaggedResourceLimitAndCountResponse;
import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -106,6 +107,8 @@ public class ResourceLimitManagerImplTest extends TestCase {
@Mock @Mock
ResourceCountDao resourceCountDao; ResourceCountDao resourceCountDao;
@Mock @Mock
private ReservationDao reservationDao;
@Mock
UserVmJoinDao userVmJoinDao; UserVmJoinDao userVmJoinDao;
@Mock @Mock
ServiceOfferingDao serviceOfferingDao; ServiceOfferingDao serviceOfferingDao;
@ -675,7 +678,9 @@ public class ResourceLimitManagerImplTest extends TestCase {
Assert.assertEquals(2L, resourceLimitManager.calculateVmCountForAccount(accountId, tag)); Assert.assertEquals(2L, resourceLimitManager.calculateVmCountForAccount(accountId, tag));
tag = "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)); 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.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@ -123,10 +124,10 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
DomainVO domain = new DomainVO(); DomainVO domain = new DomainVO();
VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class); VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class);
when(_orchSrvc.getVirtualMachine(nullable(String.class))).thenReturn(vmEntity); lenient().when(_orchSrvc.getVirtualMachine(nullable(String.class))).thenReturn(vmEntity);
when(vmEntity.destroy(nullable(Boolean.class))).thenReturn(true); 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); VolumeVO vol = new VolumeVO(VOLUME_UUID, 1l, 1l, 1l, 1l, 1l, "folder", "path", null, 50, Type.ROOT);
vol.setDisplayVolume(true); 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); 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.listByAccountId(ACCOUNT_ID)).thenReturn(Arrays.asList(vm));
lenient().when(_userVmDao.findByUuid(nullable(String.class))).thenReturn(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); ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
lenient().when(offering.getCpu()).thenReturn(500); lenient().when(offering.getCpu()).thenReturn(500);
lenient().when(offering.getId()).thenReturn(1l); 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); 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 // If the VM is already destroyed, no events should get emitted
public void destroyedVMRootVolumeUsageEvent() public void destroyedVMRootVolumeUsageEvent()
throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { 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); List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(true);
Assert.assertEquals(0, emittedEvents.size()); Assert.assertEquals(0, emittedEvents.size());
} }
@Ignore()
@Test @Test
// If the VM is running, we should see one emitted event for the root // If the VM is running, we should see one emitted event for the root
// volume. // volume.
public void runningVMRootVolumeUsageEvent() public void runningVMRootVolumeUsageEvent()
throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException {
Mockito.doNothing().when(vmStatsDaoMock).removeAllByVmId(Mockito.anyLong()); 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); List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(false);
UsageEventVO event = emittedEvents.get(0); UsageEventVO event = emittedEvents.get(0);
Assert.assertEquals(EventTypes.EVENT_VOLUME_DELETE, event.getType()); Assert.assertEquals(EventTypes.EVENT_VOLUME_DELETE, event.getType());
Assert.assertEquals(VOLUME_UUID, event.getResourceName()); Assert.assertEquals(VOLUME_UUID, event.getResourceName());
} }
} }

View File

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