cloudstack/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
Abhishek Kumar 0b5a5e8043
api,agent,server,engine-schema: scalability improvements (#9840)
* api,agent,server,engine-schema: scalability improvements

Following changes and improvements have been added:

- Improvements in handling of PingRoutingCommand

    1. Added global config - `vm.sync.power.state.transitioning`, default value: true, to control syncing of power states for transitioning VMs. This can be set to false to prevent computation of transitioning state VMs.
    2. Improved VirtualMachinePowerStateSync to allow power state sync for host VMs in a batch
    3. Optimized scanning stalled VMs

- Added option to set worker threads for capacity calculation using config - `capacity.calculate.workers`

- Added caching framework based on Caffeine in-memory caching library, https://github.com/ben-manes/caffeine

- Added caching for account/use role API access with expiration after write can be configured using config - `dynamic.apichecker.cache.period`. If set to zero then there will be no caching. Default is 0.

- Added caching for account/use role API access with expiration after write set to 60 seconds.

- Added caching for some recurring DB retrievals

    1. CapacityManager - listing service offerings - beneficial in host capacity calculation
    2. LibvirtServerDiscoverer existing host for the cluster - beneficial for host joins
    3. DownloadListener - hypervisors for zone - beneficial for host joins
    5. VirtualMachineManagerImpl - VMs in progress- beneficial for processing stalled VMs during PingRoutingCommands

- Optimized MS list retrieval for agent connect

- Optimize finding ready systemvm template for zone

- Database retrieval optimisations - fix and refactor for cases where only IDs or counts are used mainly for hosts and other infra entities. Also similar cases for VMs and other entities related to host concerning background tasks

- Changes in agent-agentmanager connection with NIO client-server classes

    1. Optimized the use of the executor service
    2. Refactore Agent class to better handle connections.
    3. Do SSL handshakes within worker threads
    5. Added global configs to control the behaviour depending on the infra. SSL handshake could be a bottleneck during agent connections. Configs - `agent.ssl.handshake.min.workers` and `agent.ssl.handshake.max.workers` can be used to control number of new connections management server handles at a time. `agent.ssl.handshake.timeout` can be used to set number of seconds after which SSL handshake times out at MS end.
    6. On agent side backoff and sslhandshake timeout can be controlled by agent properties. `backoff.seconds` and `ssl.handshake.timeout` properties can be used.

- Improvements in StatsCollection - minimize DB retrievals.

- Improvements in DeploymentPlanner allow for the retrieval of only desired host fields and fewer retrievals.

- Improvements in hosts connection for a storage pool. Added config - `storage.pool.host.connect.workers` to control the number of worker threads that can be used to connect hosts to a storage pool. Worker thread approach is followed currently only for NFS and ScaleIO pools.

- Minor improvements in resource limit calculations wrt DB retrievals

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>

* test1, domaindetails, capacitymanager fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* test2 - agent tests

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* capacitymanagertest fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* change

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* fix missing changes

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* address comments

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* revert marvin/setup.py

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* fix indent

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* use space in sql

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* address duplicate

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* update host logs

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* revert e36c6a5d07

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* fix npe in capacity calculation

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* move schema changes to 4.20.1 upgrade

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* build fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* address comments

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* fix build

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* add some more tests

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* checkstyle fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* remove unnecessary mocks

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* build fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* replace statics

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* engine/orchestration,utils: limit number of concurrent new agent
connections

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* refactor - remove unused

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* unregister closed connections, monitor & cleanup

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* add check for outdated vm filter in power sync

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* agent: synchronize sendRequest wait

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

---------

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2025-02-01 12:28:41 +05:30

2217 lines
108 KiB
Java

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.resourcelimit;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.utils.Ternary;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ResourceLimitAndCountResponse;
import org.apache.cloudstack.api.response.TaggedResourceLimitAndCountResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.ConfigKeyScheduledExecutionWrapper;
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;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.cloud.alert.AlertManager;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.configuration.Config;
import com.cloud.configuration.Resource;
import com.cloud.configuration.Resource.ResourceOwnerType;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.configuration.ResourceCount;
import com.cloud.configuration.ResourceCountVO;
import com.cloud.configuration.ResourceLimitVO;
import com.cloud.configuration.dao.ResourceCountDao;
import com.cloud.configuration.dao.ResourceLimitDao;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.VlanDao;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectAccount.Role;
import com.cloud.projects.dao.ProjectAccountDao;
import com.cloud.projects.dao.ProjectDao;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase.SumCount;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria.Func;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
@Component
public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLimitService, Configurable {
@Inject
private AccountManager _accountMgr;
@Inject
private AlertManager _alertMgr;
@Inject
AccountDao _accountDao;
@Inject
private ConfigurationDao _configDao;
@Inject
private DomainDao _domainDao;
@Inject
private EntityManager _entityMgr;
@Inject
private IPAddressDao _ipAddressDao;
@Inject
private NetworkDao _networkDao;
@Inject
private ProjectDao _projectDao;
@Inject
private ProjectAccountDao _projectAccountDao;
@Inject
private ResourceCountDao _resourceCountDao;
@Inject
private ResourceLimitDao _resourceLimitDao;
@Inject
private ResourceLimitService resourceLimitService;
@Inject
private ReservationDao reservationDao;
@Inject
protected SnapshotDao _snapshotDao;
@Inject
private SnapshotDataStoreDao _snapshotDataStoreDao;
@Inject
private TemplateDataStoreDao _vmTemplateStoreDao;
@Inject
private UserVmDao _userVmDao;
@Inject
private UserVmJoinDao _userVmJoinDao;
@Inject
private VMInstanceDao _vmDao;
@Inject
protected VMTemplateDao _vmTemplateDao;
@Inject
private VolumeDao _volumeDao;
@Inject
private VpcDao _vpcDao;
@Inject
private VlanDao _vlanDao;
@Inject
private ManagementServerHostDao managementServerHostDao;
@Inject
ServiceOfferingDao serviceOfferingDao;
@Inject
DiskOfferingDao diskOfferingDao;
protected GenericSearchBuilder<TemplateDataStoreVO, SumCount> templateSizeSearch;
protected GenericSearchBuilder<SnapshotDataStoreVO, SumCount> snapshotSizeSearch;
protected SearchBuilder<ResourceCountVO> ResourceCountSearch;
ScheduledExecutorService _rcExecutor;
Map<String, Long> accountResourceLimitMap = new HashMap<>();
Map<String, Long> domainResourceLimitMap = new HashMap<>();
Map<String, Long> projectResourceLimitMap = new HashMap<>();
@SuppressWarnings("unchecked")
protected void removeResourceReservationIfNeededAndIncrementResourceCount(final long accountId, final ResourceType type, String tag, final long numToIncrement) {
Object obj = CallContext.current().getContextParameter(CheckedReservation.getResourceReservationContextParameterKey(type));
List<Long> reservationIds = (List<Long>)obj; // This complains an unchecked casting warning
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException {
reservationDao.removeByIds(reservationIds);
if (!updateResourceCountForAccount(accountId, type, tag, true, numToIncrement)) {
// we should fail the operation (resource creation) when failed to update the resource count
throw new CloudRuntimeException("Failed to increment resource count of type " + type + " for account id=" + accountId);
}
}
});
}
private void cleanupResourceReservationsForMs() {
int reservationsRemoved = reservationDao.removeByMsId(ManagementServerNode.getManagementServerId());
if (reservationsRemoved > 0) {
logger.warn("Removed {} resource reservations for management server id {}", reservationsRemoved, ManagementServerNode.getManagementServerId());
}
}
@Override
public boolean start() {
cleanupResourceReservationsForMs();
if (ResourceCountCheckInterval.value() >= 0) {
ConfigKeyScheduledExecutionWrapper runner = new ConfigKeyScheduledExecutionWrapper(_rcExecutor, new ResourceCountCheckTask(), ResourceCountCheckInterval, TimeUnit.SECONDS);
runner.start();
}
return true;
}
@Override
public boolean stop() {
if (_rcExecutor != null) {
_rcExecutor.shutdown();
}
cleanupResourceReservationsForMs();
return true;
}
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
ResourceCountSearch = _resourceCountDao.createSearchBuilder();
ResourceCountSearch.and("id", ResourceCountSearch.entity().getId(), SearchCriteria.Op.IN);
ResourceCountSearch.and("accountId", ResourceCountSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
ResourceCountSearch.and("domainId", ResourceCountSearch.entity().getDomainId(), SearchCriteria.Op.EQ);
ResourceCountSearch.done();
templateSizeSearch = _vmTemplateStoreDao.createSearchBuilder(SumCount.class);
templateSizeSearch.select("sum", Func.SUM, templateSizeSearch.entity().getSize());
templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.EQ);
templateSizeSearch.and("destroyed", templateSizeSearch.entity().getDestroyed(), Op.EQ);
SearchBuilder<VMTemplateVO> join1 = _vmTemplateDao.createSearchBuilder();
join1.and("accountId", join1.entity().getAccountId(), Op.EQ);
templateSizeSearch.join("templates", join1, templateSizeSearch.entity().getTemplateId(), join1.entity().getId(), JoinBuilder.JoinType.INNER);
templateSizeSearch.done();
snapshotSizeSearch = _snapshotDataStoreDao.createSearchBuilder(SumCount.class);
snapshotSizeSearch.select("sum", Func.SUM, snapshotSizeSearch.entity().getPhysicalSize());
snapshotSizeSearch.and("state", snapshotSizeSearch.entity().getState(), Op.EQ);
snapshotSizeSearch.and("storeRole", snapshotSizeSearch.entity().getRole(), Op.EQ);
SearchBuilder<SnapshotVO> join2 = _snapshotDao.createSearchBuilder();
join2.and("accountId", join2.entity().getAccountId(), Op.EQ);
snapshotSizeSearch.join("snapshots", join2, snapshotSizeSearch.entity().getSnapshotId(), join2.entity().getId(), JoinBuilder.JoinType.INNER);
snapshotSizeSearch.done();
if (ResourceCountCheckInterval.value() >= 0) {
_rcExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ResourceCountChecker"));
}
try {
projectResourceLimitMap.put(Resource.ResourceType.public_ip.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectPublicIPs.key())));
projectResourceLimitMap.put(Resource.ResourceType.snapshot.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectSnapshots.key())));
projectResourceLimitMap.put(Resource.ResourceType.template.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectTemplates.key())));
projectResourceLimitMap.put(Resource.ResourceType.user_vm.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectUserVms.key())));
projectResourceLimitMap.put(Resource.ResourceType.volume.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectVolumes.key())));
projectResourceLimitMap.put(Resource.ResourceType.network.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectNetworks.key())));
projectResourceLimitMap.put(Resource.ResourceType.vpc.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectVpcs.key())));
projectResourceLimitMap.put(Resource.ResourceType.cpu.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectCpus.key())));
projectResourceLimitMap.put(Resource.ResourceType.memory.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectMemory.key())));
projectResourceLimitMap.put(Resource.ResourceType.primary_storage.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectPrimaryStorage.key())));
projectResourceLimitMap.put(Resource.ResourceType.secondary_storage.name(), MaxProjectSecondaryStorage.value());
accountResourceLimitMap.put(Resource.ResourceType.public_ip.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountPublicIPs.key())));
accountResourceLimitMap.put(Resource.ResourceType.snapshot.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountSnapshots.key())));
accountResourceLimitMap.put(Resource.ResourceType.template.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountTemplates.key())));
accountResourceLimitMap.put(Resource.ResourceType.user_vm.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountUserVms.key())));
accountResourceLimitMap.put(Resource.ResourceType.volume.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountVolumes.key())));
accountResourceLimitMap.put(Resource.ResourceType.network.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountNetworks.key())));
accountResourceLimitMap.put(Resource.ResourceType.vpc.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountVpcs.key())));
accountResourceLimitMap.put(Resource.ResourceType.cpu.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountCpus.key())));
accountResourceLimitMap.put(Resource.ResourceType.memory.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountMemory.key())));
accountResourceLimitMap.put(Resource.ResourceType.primary_storage.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountPrimaryStorage.key())));
accountResourceLimitMap.put(Resource.ResourceType.secondary_storage.name(), MaxAccountSecondaryStorage.value());
accountResourceLimitMap.put(Resource.ResourceType.project.name(), DefaultMaxAccountProjects.value());
domainResourceLimitMap.put(Resource.ResourceType.public_ip.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainPublicIPs.key())));
domainResourceLimitMap.put(Resource.ResourceType.snapshot.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainSnapshots.key())));
domainResourceLimitMap.put(Resource.ResourceType.template.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainTemplates.key())));
domainResourceLimitMap.put(Resource.ResourceType.user_vm.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainUserVms.key())));
domainResourceLimitMap.put(Resource.ResourceType.volume.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainVolumes.key())));
domainResourceLimitMap.put(Resource.ResourceType.network.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainNetworks.key())));
domainResourceLimitMap.put(Resource.ResourceType.vpc.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainVpcs.key())));
domainResourceLimitMap.put(Resource.ResourceType.cpu.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainCpus.key())));
domainResourceLimitMap.put(Resource.ResourceType.memory.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainMemory.key())));
domainResourceLimitMap.put(Resource.ResourceType.primary_storage.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainPrimaryStorage.key())));
domainResourceLimitMap.put(Resource.ResourceType.secondary_storage.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainSecondaryStorage.key())));
domainResourceLimitMap.put(Resource.ResourceType.project.name(), DefaultMaxDomainProjects.value());
} catch (NumberFormatException e) {
logger.error("NumberFormatException during configuration", e);
throw new ConfigurationException("Configuration failed due to NumberFormatException, see log for the stacktrace");
}
return true;
}
@Override
public void incrementResourceCountWithTag(long accountId, ResourceType type, String tag, Long... delta) {
// don't upgrade resource count for system account
if (accountId == Account.ACCOUNT_ID_SYSTEM) {
logger.trace("Not incrementing resource count for system accounts, returning");
return;
}
final long numToIncrement = (delta.length == 0) ? 1 : delta[0].longValue();
removeResourceReservationIfNeededAndIncrementResourceCount(accountId, type, tag, numToIncrement);
}
@Override
public void incrementResourceCount(long accountId, ResourceType type, Long... delta) {
incrementResourceCountWithTag(accountId, type, null, delta);
}
@Override
public void decrementResourceCountWithTag(long accountId, ResourceType type, String tag, Long... delta) {
// don't upgrade resource count for system account
if (accountId == Account.ACCOUNT_ID_SYSTEM) {
logger.trace("Not decrementing resource count for system accounts, returning");
return;
}
long numToDecrement = (delta.length == 0) ? 1 : delta[0].longValue();
if (!updateResourceCountForAccount(accountId, type, tag, false, numToDecrement)) {
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, "Failed to decrement resource count of type " + type + " for account id=" + accountId,
"Failed to decrement resource count of type " + type + " for account id=" + accountId + "; use updateResourceCount API to recalculate/fix the problem");
}
}
@Override
public void decrementResourceCount(long accountId, ResourceType type, Long... delta) {
decrementResourceCountWithTag(accountId, type, null, delta);
}
@Override
public long findCorrectResourceLimitForAccount(Account account, ResourceType type, String tag) {
long max = Resource.RESOURCE_UNLIMITED; // if resource limit is not found, then we treat it as unlimited
// No limits for Root Admin accounts
if (_accountMgr.isRootAdmin(account.getId())) {
return max;
}
ResourceLimitVO limit = _resourceLimitDao.findByOwnerIdAndTypeAndTag(account.getId(), ResourceOwnerType.Account, type, tag);
// Check if limit is configured for account
if (limit != null) {
max = limit.getMax().longValue();
} else {
String resourceTypeName = type.name();
// If the account has an no limit set, then return global default account limits
Long value = null;
if (account.getType() == Account.Type.PROJECT) {
value = projectResourceLimitMap.get(resourceTypeName);
} else {
if (StringUtils.isNotEmpty(tag)) {
return findCorrectResourceLimitForAccount(account, type, null);
}
value = accountResourceLimitMap.get(resourceTypeName);
}
if (value != null) {
if (value < 0) { // return unlimit if value is set to negative
return max;
}
// convert the value from GiB to bytes in case of primary or secondary storage.
if (type == ResourceType.primary_storage || type == ResourceType.secondary_storage) {
value = value * ResourceType.bytesToGiB;
}
return value;
}
}
return max;
}
@Override
public long findCorrectResourceLimitForAccount(long accountId, Long limit, ResourceType type) {
long max = Resource.RESOURCE_UNLIMITED; // if resource limit is not found, then we treat it as unlimited
// No limits for Root Admin accounts
if (_accountMgr.isRootAdmin(accountId)) {
return max;
}
Account account = _accountDao.findById(accountId);
if (account == null) {
return max;
}
// Check if limit is configured for account
if (limit != null) {
max = limit.longValue();
} else {
// If the account has an no limit set, then return global default account limits
Long value = null;
if (account.getType() == Account.Type.PROJECT) {
value = projectResourceLimitMap.get(type.getName());
} else {
value = accountResourceLimitMap.get(type.getName());
}
if (value != null) {
if (value < 0) { // return unlimit if value is set to negative
return max;
}
if (type == ResourceType.primary_storage || type == ResourceType.secondary_storage) {
value = value * ResourceType.bytesToGiB;
}
return value;
}
}
return max;
}
@Override
public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type, String tag) {
long max = Resource.RESOURCE_UNLIMITED;
// no limits on ROOT domain
if (domain.getId() == Domain.ROOT_DOMAIN) {
return Resource.RESOURCE_UNLIMITED;
}
// Check account
ResourceLimitVO limit = _resourceLimitDao.findByOwnerIdAndTypeAndTag(domain.getId(), ResourceOwnerType.Domain, type, tag);
if (limit != null) {
max = limit.getMax().longValue();
} else {
// check domain hierarchy
Long domainId = domain.getParent();
while ((domainId != null) && (limit == null)) {
if (domainId == Domain.ROOT_DOMAIN) {
break;
}
limit = _resourceLimitDao.findByOwnerIdAndTypeAndTag(domainId, ResourceOwnerType.Domain, type, tag);
DomainVO tmpDomain = _domainDao.findById(domainId);
domainId = tmpDomain.getParent();
}
if (limit != null) {
max = limit.getMax().longValue();
} else {
if (StringUtils.isNotEmpty(tag)) {
return findCorrectResourceLimitForDomain(domain, type, null);
}
Long value = null;
value = domainResourceLimitMap.get(type.name());
if (value != null) {
if (value < 0) { // return unlimit if value is set to negative
return max;
}
if (type == ResourceType.primary_storage || type == ResourceType.secondary_storage) {
value = value * ResourceType.bytesToGiB;
}
return value;
}
}
}
return max;
}
protected void checkDomainResourceLimit(final Account account, final Project project, final ResourceType type, String tag, long numResources) throws ResourceAllocationException {
// check all domains in the account's domain hierarchy
Long domainId = null;
if (project != null) {
domainId = project.getDomainId();
} else {
domainId = account.getDomainId();
}
while (domainId != null) {
DomainVO domain = _domainDao.findById(domainId);
// no limit check if it is ROOT domain
if (domainId != Domain.ROOT_DOMAIN) {
long domainResourceLimit = findCorrectResourceLimitForDomain(domain, type, tag);
long currentDomainResourceCount = _resourceCountDao.getResourceCount(domainId, ResourceOwnerType.Domain, type, tag);
long currentResourceReservation = reservationDao.getDomainReservation(domainId, type, tag);
long requestedDomainResourceCount = currentDomainResourceCount + currentResourceReservation + numResources;
String convDomainResourceLimit = String.valueOf(domainResourceLimit);
String convCurrentDomainResourceCount = String.valueOf(currentDomainResourceCount);
String convCurrentResourceReservation = String.valueOf(currentResourceReservation);
String convNumResources = String.valueOf(numResources);
if (type == ResourceType.secondary_storage || type == ResourceType.primary_storage){
convDomainResourceLimit = toHumanReadableSize(domainResourceLimit);
convCurrentDomainResourceCount = toHumanReadableSize(currentDomainResourceCount);
convCurrentResourceReservation = toHumanReadableSize(currentResourceReservation);
convNumResources = toHumanReadableSize(numResources);
}
String typeString = type.getName();
if (StringUtils.isNotEmpty(tag)) {
typeString = String.format("%s (tag: %s)", typeString, tag);
}
String messageSuffix = String.format(
" domain resource limits of Type '%s' for Domain Id = %s is exceeded: Domain Resource Limit = %s, " +
"Current Domain Resource Amount = %s, Current Resource Reservation = %s, Requested Resource Amount = %s.",
typeString, domain.getUuid(), convDomainResourceLimit,
convCurrentDomainResourceCount, convCurrentResourceReservation, convNumResources
);
if (logger.isDebugEnabled()) {
logger.debug("Checking if" + messageSuffix);
}
if (domainResourceLimit != Resource.RESOURCE_UNLIMITED && requestedDomainResourceCount > domainResourceLimit) {
String message = "Maximum" + messageSuffix;
ResourceAllocationException e = new ResourceAllocationException(message, type);
logger.error(message, e);
throw e;
}
}
domainId = domain.getParent();
}
}
protected void checkAccountResourceLimit(final Account account, final Project project, final ResourceType type, String tag, long numResources) throws ResourceAllocationException {
// Check account limits
long accountResourceLimit = findCorrectResourceLimitForAccount(account, type, tag);
long currentResourceCount = _resourceCountDao.getResourceCount(account.getId(), ResourceOwnerType.Account, type, tag);
long currentResourceReservation = reservationDao.getAccountReservation(account.getId(), type, tag);
long requestedResourceCount = currentResourceCount + currentResourceReservation + numResources;
String convertedAccountResourceLimit = String.valueOf(accountResourceLimit);
String convertedCurrentResourceCount = String.valueOf(currentResourceCount);
String convertedCurrentResourceReservation = String.valueOf(currentResourceReservation);
String convertedNumResources = String.valueOf(numResources);
if (type == ResourceType.secondary_storage || type == ResourceType.primary_storage){
convertedAccountResourceLimit = toHumanReadableSize(accountResourceLimit);
convertedCurrentResourceCount = toHumanReadableSize(currentResourceCount);
convertedCurrentResourceReservation = toHumanReadableSize(currentResourceReservation);
convertedNumResources = toHumanReadableSize(numResources);
}
String messageSuffix = String.format(
" amount of resources of Type = '%s', tag = '%s' for %s in Domain Id = %s is exceeded: " +
"Account Resource Limit = %s, Current Account Resource Amount = %s, Current Account Resource Reservation = %s, Requested Resource Amount = %s.",
type, tag, (project == null ? "Account Name = " + account.getAccountName() : "Project Name = " + project.getName()), account.getDomainId(),
convertedAccountResourceLimit, convertedCurrentResourceCount, convertedCurrentResourceReservation, convertedNumResources
);
if (logger.isDebugEnabled()) {
logger.debug("Checking if" + messageSuffix);
}
if (accountResourceLimit != Resource.RESOURCE_UNLIMITED && requestedResourceCount > accountResourceLimit) {
String message = "Maximum" + messageSuffix;
ResourceAllocationException e = new ResourceAllocationException(message, type);
logger.error(message, e);
throw e;
}
}
protected List<ResourceCountVO> lockAccountAndOwnerDomainRows(long accountId, final ResourceType type, String tag) {
Set<Long> rowIdsToLock = _resourceCountDao.listAllRowsToUpdate(accountId, ResourceOwnerType.Account, type, tag);
SearchCriteria<ResourceCountVO> sc = ResourceCountSearch.create();
sc.setParameters("id", rowIdsToLock.toArray());
return _resourceCountDao.lockRows(sc, null, true);
}
@Override
public long findDefaultResourceLimitForDomain(ResourceType resourceType) {
Long resourceLimit = null;
resourceLimit = domainResourceLimitMap.get(resourceType.getName());
if (resourceLimit != null && (resourceType == ResourceType.primary_storage || resourceType == ResourceType.secondary_storage)) {
if (! Long.valueOf(Resource.RESOURCE_UNLIMITED).equals(resourceLimit)) {
resourceLimit = resourceLimit * ResourceType.bytesToGiB;
}
} else {
resourceLimit = Long.valueOf(Resource.RESOURCE_UNLIMITED);
}
return resourceLimit;
}
@Override
public long findCorrectResourceLimitForAccountAndDomain(Account account, Domain domain, ResourceType type, String tag) {
long maxSecondaryStorageForAccount = findCorrectResourceLimitForAccount(account, type, tag);
long maxSecondaryStorageForDomain = findCorrectResourceLimitForDomain(domain, type, tag);
if (maxSecondaryStorageForDomain == Resource.RESOURCE_UNLIMITED || maxSecondaryStorageForAccount == Resource.RESOURCE_UNLIMITED) {
return Math.max(maxSecondaryStorageForDomain, maxSecondaryStorageForAccount);
}
return Math.min(maxSecondaryStorageForDomain, maxSecondaryStorageForAccount);
}
@Override
public void checkResourceLimit(final Account account, final ResourceType type, long... count) throws ResourceAllocationException {
checkResourceLimitWithTag(account, type, null, count);
}
@Override
public void checkResourceLimitWithTag(final Account account, final ResourceType type, String tag, long... count) throws ResourceAllocationException {
final long numResources = ((count.length == 0) ? 1 : count[0]);
Project project = null;
// Don't place any limits on system or root admin accounts
if (_accountMgr.isRootAdmin(account.getId())) {
return;
}
if (account.getType() == Account.Type.PROJECT) {
project = _projectDao.findByProjectAccountId(account.getId());
}
final Project projectFinal = project;
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<ResourceAllocationException>() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException {
// Lock all rows first so nobody else can read it
lockAccountAndOwnerDomainRows(account.getId(), type, tag);
// Check account limits
checkAccountResourceLimit(account, projectFinal, type, tag, numResources);
// check all domains in the account's domain hierarchy
checkDomainResourceLimit(account, projectFinal, type, tag, numResources);
}
});
}
/**
* To retrieve host and storage limit tags lists with or without a given tag string
* while searching for limits for an account or domain
* @param tag - tag string to filter list of host and storage limit tags
* @return a pair of host tags list and storage tags list
*/
protected Pair<List<String>, List<String>> getResourceLimitTagsForLimitSearch(String tag) {
List<String> hostTags = getResourceLimitHostTags();
List<String> storageTags = getResourceLimitStorageTags();
if (tag == null) {
return new Pair<>(hostTags, storageTags);
}
if (hostTags.contains(tag)) {
hostTags = List.of(tag);
} else {
hostTags = new ArrayList<>();
}
if (storageTags.contains(tag)) {
storageTags = List.of(tag);
} else {
storageTags = new ArrayList<>();
}
return new Pair<>(hostTags, storageTags);
}
@Override
public List<ResourceLimitVO> searchForLimits(Long id, Long accountId, Long domainId, ResourceType resourceType, String tag, Long startIndex, Long pageSizeVal) {
Account caller = CallContext.current().getCallingAccount();
List<ResourceLimitVO> limits = new ArrayList<ResourceLimitVO>();
boolean isAccount = true;
if (!_accountMgr.isAdmin(caller.getId())) {
accountId = caller.getId();
domainId = null;
} else {
if (domainId != null) {
// verify domain information and permissions
Domain domain = _domainDao.findById(domainId);
if (domain == null) {
// return empty set
return limits;
}
_accountMgr.checkAccess(caller, domain);
if (accountId != null) {
// Verify account information and permissions
Account account = _accountDao.findById(accountId);
if (account == null) {
// return empty set
return limits;
}
_accountMgr.checkAccess(caller, null, true, account);
domainId = null;
}
}
}
// If id is passed in, get the record and return it if permission check has passed
if (id != null) {
ResourceLimitVO vo = _resourceLimitDao.findById(id);
if (vo.getAccountId() != null) {
_accountMgr.checkAccess(caller, null, true, _accountDao.findById(vo.getAccountId()));
limits.add(vo);
} else if (vo.getDomainId() != null) {
_accountMgr.checkAccess(caller, _domainDao.findById(vo.getDomainId()));
limits.add(vo);
}
return limits;
}
// If account is not specified, default it to caller account
if (accountId == null) {
if (domainId == null) {
accountId = caller.getId();
isAccount = true;
} else {
isAccount = false;
}
} else {
isAccount = true;
}
SearchBuilder<ResourceLimitVO> sb = _resourceLimitDao.createSearchBuilder();
sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.and("tag", sb.entity().getTag(), SearchCriteria.Op.EQ);
SearchCriteria<ResourceLimitVO> sc = sb.create();
Filter filter = new Filter(ResourceLimitVO.class, "id", true, startIndex, pageSizeVal);
if (accountId != null) {
sc.setParameters("accountId", accountId);
}
if (domainId != null) {
sc.setParameters("domainId", domainId);
sc.setParameters("accountId", (Object[])null);
}
if (resourceType != null) {
sc.setParameters("type", resourceType);
}
if (tag != null) {
sc.setParameters("tag", tag);
}
List<ResourceLimitVO> foundLimits = _resourceLimitDao.search(sc, filter);
Pair<List<String>, List<String>> tagsPair = getResourceLimitTagsForLimitSearch(tag);
List<String> hostTags = tagsPair.first();
List<String> storageTags = tagsPair.second();
if (resourceType != null) {
if (foundLimits.isEmpty()) {
ResourceOwnerType ownerType = ResourceOwnerType.Domain;
Long ownerId = domainId;
long max = 0;
if (isAccount) {
ownerType = ResourceOwnerType.Account;
ownerId = accountId;
max = findCorrectResourceLimitForAccount(_accountMgr.getAccount(accountId), resourceType, tag);
} else {
max = findCorrectResourceLimitForDomain(_domainDao.findById(domainId), resourceType, tag);
}
limits.add(new ResourceLimitVO(resourceType, max, ownerId, ownerType));
} else {
limits.addAll(foundLimits);
}
} else {
limits.addAll(foundLimits);
// see if any limits are missing from the table, and if yes - get it from the config table and add
ResourceType[] resourceTypes = ResourceCount.ResourceType.values();
if (foundLimits.size() != resourceTypes.length) {
List<String> accountLimitStr = new ArrayList<String>();
List<String> domainLimitStr = new ArrayList<String>();
for (ResourceLimitVO foundLimit : foundLimits) {
if (foundLimit.getAccountId() != null) {
accountLimitStr.add(foundLimit.getType().toString());
} else {
domainLimitStr.add(foundLimit.getType().toString());
}
}
// get default from config values
if (isAccount) {
if (accountLimitStr.size() < resourceTypes.length) {
for (ResourceType rt : resourceTypes) {
if (!accountLimitStr.contains(rt.toString())) {
limits.add(new ResourceLimitVO(rt, findCorrectResourceLimitForAccount(_accountMgr.getAccount(accountId), rt, null), accountId, ResourceOwnerType.Account));
}
}
}
} else {
if (domainLimitStr.size() < resourceTypes.length) {
for (ResourceType rt : resourceTypes) {
if (!domainLimitStr.contains(rt.toString())) {
limits.add(new ResourceLimitVO(rt, findCorrectResourceLimitForDomain(_domainDao.findById(domainId), rt, null), domainId, ResourceOwnerType.Domain));
}
}
}
}
}
}
addTaggedResourceLimits(limits, resourceType, isAccount ? ResourceOwnerType.Account : ResourceOwnerType.Domain, isAccount ? accountId : domainId, hostTags, storageTags);
return limits;
}
protected void addTaggedResourceLimits(List<ResourceLimitVO> limits, List<ResourceType> types, List<String> tags, ResourceOwnerType ownerType, long ownerId) {
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (CollectionUtils.isEmpty(types)) {
return;
}
for (String tag : tags) {
for (ResourceType type : types) {
if (limits.stream().noneMatch(l -> type.equals(l.getType()) && tag.equals(l.getTag()))) {
limits.add(new ResourceLimitVO(type, ResourceOwnerType.Domain.equals(ownerType) ?
findCorrectResourceLimitForDomain(_domainDao.findById(ownerId), type, tag) :
findCorrectResourceLimitForAccount(_accountDao.findById(ownerId), type, tag),
ownerId, ownerType, tag));
}
}
}
}
protected void removeUndesiredTaggedLimits(List<ResourceLimitVO> limits, List<String> hostTags, List<String> storageTags) {
Iterator<ResourceLimitVO> itr = limits.iterator();
while (itr.hasNext()) {
ResourceLimitVO limit = itr.next();
if (StringUtils.isEmpty(limit.getTag())) {
continue;
}
if (HostTagsSupportingTypes.contains(limit.getType()) &&
(CollectionUtils.isEmpty(hostTags) || !hostTags.contains(limit.getTag()))) {
itr.remove();
}
if (StorageTagsSupportingTypes.contains(limit.getType()) &&
(CollectionUtils.isEmpty(storageTags) || !storageTags.contains(limit.getTag()))) {
itr.remove();
}
}
}
protected void addTaggedResourceLimits(List<ResourceLimitVO> limits, ResourceType resourceType, ResourceOwnerType ownerType, long ownerId, List<String> hostTags, List<String> storageTags) {
removeUndesiredTaggedLimits(limits, hostTags, storageTags);
if (CollectionUtils.isEmpty(hostTags) && CollectionUtils.isEmpty(storageTags)) {
return;
}
List<ResourceType> types = resourceType != null ? HostTagsSupportingTypes.contains(resourceType) ? List.of(resourceType) : null : HostTagsSupportingTypes;
addTaggedResourceLimits(limits, types, hostTags, ownerType, ownerId);
types = resourceType != null ? StorageTagsSupportingTypes.contains(resourceType) ? List.of(resourceType) : null : StorageTagsSupportingTypes;
addTaggedResourceLimits(limits, types, storageTags, ownerType, ownerId);
limits.sort((o1, o2) -> {
Integer type1 = o1.getType().getOrdinal();
Integer type2 = o2.getType().getOrdinal();
if (type1.equals(type2)) {
return StringUtils.defaultString(o1.getTag(), "").compareTo(StringUtils.defaultString(o2.getTag(), ""));
}
return type1.compareTo(type2);
});
}
@Override
public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Integer typeId, Long max, String tag) {
Account caller = CallContext.current().getCallingAccount();
if (max == null) {
max = new Long(Resource.RESOURCE_UNLIMITED);
} else if (max.longValue() < Resource.RESOURCE_UNLIMITED) {
throw new InvalidParameterValueException("Please specify either '-1' for an infinite limit, or a limit that is at least '0'.");
}
// Map resource type
ResourceType resourceType = null;
if (typeId != null) {
for (ResourceType type : Resource.ResourceType.values()) {
if (type.getOrdinal() == typeId.intValue()) {
resourceType = type;
}
}
if (resourceType == null) {
throw new InvalidParameterValueException("Please specify valid resource type");
}
}
if (StringUtils.isNotEmpty(tag) &&
!(HostTagsSupportingTypes.contains(resourceType) ||
StorageTagsSupportingTypes.contains(resourceType))) {
throw new InvalidParameterValueException(String.format("Resource limit with a tag is not supported for resource type %d", typeId));
}
//Convert max storage size from GiB to bytes
if ((resourceType == ResourceType.primary_storage || resourceType == ResourceType.secondary_storage) && max >= 0) {
max *= ResourceType.bytesToGiB;
}
ResourceOwnerType ownerType = null;
Long ownerId = null;
if (accountId != null) {
Account account = _entityMgr.findById(Account.class, accountId);
if (account == null) {
throw new InvalidParameterValueException("Unable to find account " + accountId);
}
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("Can't update system account");
}
//only Unlimited value is accepted if account is Root Admin
if (_accountMgr.isRootAdmin(account.getId()) && max.shortValue() != Resource.RESOURCE_UNLIMITED) {
throw new InvalidParameterValueException("Only " + Resource.RESOURCE_UNLIMITED + " limit is supported for Root Admin accounts");
}
if ((caller.getAccountId() == accountId.longValue()) && (_accountMgr.isDomainAdmin(caller.getId()) || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN)) {
// If the admin is trying to update their own account, disallow.
throw new PermissionDeniedException(String.format("Unable to update resource limit for their own account %s, permission denied", account));
}
if (account.getType() == Account.Type.PROJECT) {
_accountMgr.checkAccess(caller, AccessType.ModifyProject, true, account);
} else {
_accountMgr.checkAccess(caller, null, true, account);
}
ownerType = ResourceOwnerType.Account;
ownerId = accountId;
if (StringUtils.isNotEmpty(tag)) {
long untaggedLimit = findCorrectResourceLimitForAccount(account, resourceType, null);
if (untaggedLimit > 0 && max > untaggedLimit) {
throw new InvalidParameterValueException(String.format("Maximum untagged resource limit for account %s for resource type %s is %d, please specify a value less than or equal to that",
account.getAccountName(), resourceType, untaggedLimit));
}
}
} else if (domainId != null) {
Domain domain = _entityMgr.findById(Domain.class, domainId);
_accountMgr.checkAccess(caller, domain);
if (Domain.ROOT_DOMAIN == domainId.longValue()) {
// no one can add limits on ROOT domain, disallow...
throw new PermissionDeniedException("Cannot update resource limit for ROOT domain " + domainId + ", permission denied");
}
if ((caller.getDomainId() == domainId.longValue()) && caller.getType() == Account.Type.DOMAIN_ADMIN || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN) {
// if the admin is trying to update their own domain, disallow...
throw new PermissionDeniedException("Unable to update resource limit for domain " + domainId + ", permission denied");
}
if (StringUtils.isNotEmpty(tag)) {
long untaggedLimit = findCorrectResourceLimitForDomain(domain, resourceType, null);
if (untaggedLimit > 0 && max > untaggedLimit) {
throw new InvalidParameterValueException(String.format("Maximum untagged resource limit for domain %s for resource type %s is %d, please specify a value less than or equal to that",
domain.getName(), resourceType, untaggedLimit));
}
}
Long parentDomainId = domain.getParent();
if (parentDomainId != null) {
DomainVO parentDomain = _domainDao.findById(parentDomainId);
long parentMaximum = findCorrectResourceLimitForDomain(parentDomain, resourceType, tag);
if ((parentMaximum >= 0) && (max.longValue() > parentMaximum)) {
throw new InvalidParameterValueException(String.format("Domain %s has maximum allowed resource limit %d for %s, please specify a value less than or equal to %d", parentDomain, parentMaximum, resourceType, parentMaximum));
}
}
ownerType = ResourceOwnerType.Domain;
ownerId = domainId;
}
if (ownerId == null) {
throw new InvalidParameterValueException("AccountId or domainId have to be specified in order to update resource limit");
}
ResourceLimitVO limit = _resourceLimitDao.findByOwnerIdAndTypeAndTag(ownerId, ownerType, resourceType, tag);
if (limit != null) {
// Update the existing limit
_resourceLimitDao.update(limit.getId(), max);
return _resourceLimitDao.findById(limit.getId());
} else {
return _resourceLimitDao.persist(new ResourceLimitVO(resourceType, max, ownerId, ownerType, tag));
}
}
protected boolean isTaggedResourceCountRecalculationNotNeeded(ResourceType type, List<String> hostTags, List <String> storageTags) {
if (!HostTagsSupportingTypes.contains(type) && !StorageTagsSupportingTypes.contains(type)) {
return true;
}
return CollectionUtils.isEmpty(hostTags) && CollectionUtils.isEmpty(storageTags);
}
protected void removeResourceLimitAndCountForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType,
List<String> hostTags, List<String> storageTags) {
if (logger.isDebugEnabled()) {
String msg = String.format("Clearing tagged resource limits and counts which do not match " +
"host tags: %s, storage tags: %s",
StringUtils.join(hostTags), StringUtils.join(storageTags));
if (ObjectUtils.allNotNull(ownerId, ownerType)) {
msg = String.format("%s for %s", msg, ownerType == ResourceOwnerType.Account ? _accountDao.findById(ownerId) : _domainDao.findById(ownerId));
}
logger.debug(msg);
}
_resourceLimitDao.removeResourceLimitsForNonMatchingTags(ownerId, ownerType, HostTagsSupportingTypes, hostTags);
_resourceLimitDao.removeResourceLimitsForNonMatchingTags(ownerId, ownerType, StorageTagsSupportingTypes, storageTags);
_resourceCountDao.removeResourceCountsForNonMatchingTags(ownerId, ownerType, HostTagsSupportingTypes, hostTags);
_resourceCountDao.removeResourceCountsForNonMatchingTags(ownerId, ownerType, StorageTagsSupportingTypes, storageTags);
}
protected List<ResourceCountVO> recalculateAccountTaggedResourceCount(long accountId, ResourceType type, final List<String> hostTags, final List<String> storageTags) {
List<ResourceCountVO> result = new ArrayList<>();
if (isTaggedResourceCountRecalculationNotNeeded(type, hostTags, storageTags)) {
return result;
}
if (HostTagsSupportingTypes.contains(type) && CollectionUtils.isNotEmpty(hostTags)) {
for (String tag : hostTags) {
long count = recalculateAccountResourceCount(accountId, type, tag);
result.add(new ResourceCountVO(type, count, accountId, ResourceOwnerType.Account, tag));
}
}
if (StorageTagsSupportingTypes.contains(type) && CollectionUtils.isNotEmpty(storageTags)) {
for (String tag : storageTags) {
long count = recalculateAccountResourceCount(accountId, type, tag);
result.add(new ResourceCountVO(type, count, accountId, ResourceOwnerType.Account, tag));
}
}
return result;
}
protected List<ResourceCountVO> recalculateDomainTaggedResourceCount(long domainId, ResourceType type, final List<String> hostTags, final List<String> storageTags) {
List<ResourceCountVO> result = new ArrayList<>();
if (isTaggedResourceCountRecalculationNotNeeded(type, hostTags, storageTags)) {
return result;
}
if (HostTagsSupportingTypes.contains(type) && CollectionUtils.isNotEmpty(hostTags)) {
for (String tag : hostTags) {
long count = recalculateDomainResourceCount(domainId, type, tag);
result.add(new ResourceCountVO(type, count, domainId, ResourceOwnerType.Domain, tag));
}
}
if (StorageTagsSupportingTypes.contains(type) && CollectionUtils.isNotEmpty(storageTags)) {
for (String tag : storageTags) {
long count = recalculateDomainResourceCount(domainId, type, tag);
result.add(new ResourceCountVO(type, count, domainId, ResourceOwnerType.Domain, tag));
}
}
return result;
}
@Override
public List<? extends ResourceCount> recalculateResourceCount(Long accountId, Long domainId, Integer typeId, String tag) throws CloudRuntimeException {
Account callerAccount = CallContext.current().getCallingAccount();
long count = 0;
List<ResourceCountVO> counts = new ArrayList<ResourceCountVO>();
List<ResourceType> resourceTypes = new ArrayList<ResourceType>();
ResourceType resourceType = null;
if (typeId != null) {
for (ResourceType type : Resource.ResourceType.values()) {
if (type.getOrdinal() == typeId.intValue()) {
resourceType = type;
}
}
if (resourceType == null) {
throw new InvalidParameterValueException("Please specify valid resource type");
}
if (StringUtils.isNotEmpty(tag) &&
!(HostTagsSupportingTypes.contains(resourceType) ||
StorageTagsSupportingTypes.contains(resourceType))) {
throw new InvalidParameterValueException(String.format("Resource count with a tag is not supported for resource type %d", typeId));
}
}
DomainVO domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Please specify a valid domain ID.");
}
_accountMgr.checkAccess(callerAccount, domain);
if (resourceType != null) {
resourceTypes.add(resourceType);
} else {
resourceTypes = Arrays.asList(Resource.ResourceType.values());
}
List<String> hostTags = getResourceLimitHostTags();
List<String> storageTags = getResourceLimitStorageTags();
removeResourceLimitAndCountForNonMatchingTags(accountId != null ? accountId : domainId,
accountId != null ? ResourceOwnerType.Account : ResourceOwnerType.Domain, hostTags, storageTags);
for (ResourceType type : resourceTypes) {
if (accountId != null) {
count = recalculateAccountResourceCount(accountId, type, tag);
counts.add(new ResourceCountVO(type, count, accountId, ResourceOwnerType.Account));
if (StringUtils.isEmpty(tag)) {
counts.addAll(recalculateAccountTaggedResourceCount(accountId, type, hostTags, storageTags));
}
} else {
count = recalculateDomainResourceCount(domainId, type, tag);
counts.add(new ResourceCountVO(type, count, domainId, ResourceOwnerType.Domain));
if (StringUtils.isEmpty(tag)) {
counts.addAll(recalculateDomainTaggedResourceCount(domainId, type, hostTags, storageTags));
}
}
}
return counts;
}
@Override
public List<? extends ResourceCount> recalculateResourceCount(Long accountId, Long domainId, Integer typeId) throws CloudRuntimeException {
return recalculateResourceCount(accountId, domainId, typeId, null);
}
protected boolean updateResourceCountForAccount(final long accountId, final ResourceType type, String tag, final boolean increment, final long delta) {
if (delta == 0) {
return true;
} else if (delta < 0) {
logger.warn("Resource count delta is negative, delta = {} for Account = {} Type = {} tag = {}",
delta, accountId, type, tag);
return true;
}
if (logger.isDebugEnabled()) {
String convertedDelta = String.valueOf(delta);
if (type == ResourceType.secondary_storage || type == ResourceType.primary_storage){
convertedDelta = toHumanReadableSize(delta);
}
String typeStr = StringUtils.isNotEmpty(tag) ? String.format("%s (tag: %s)", type, tag) : type.getName();
logger.debug("Updating resource Type = " + typeStr + " count for Account = " + accountId + " Operation = " + (increment ? "increasing" : "decreasing") + " Amount = " + convertedDelta);
}
Set<Long> rowIdsToUpdate = _resourceCountDao.listAllRowsToUpdate(accountId, ResourceOwnerType.Account, type, tag);
return _resourceCountDao.updateCountByDeltaForIds(new ArrayList<>(rowIdsToUpdate), increment, delta);
}
/**
* This will take care of re-calculation of resource counts for root and sub-domains
* and accounts of the sub-domains also. so just loop through immediate children of root domain
*
* @param domainId the domain level to start at
* @param type the resource type to do the recalculation for
* @return the resulting new resource count
*/
protected long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag) {
List<AccountVO> accounts = _accountDao.findActiveAccountsForDomain(domainId);
List<DomainVO> childDomains = _domainDao.findImmediateChildrenForParent(domainId);
if (CollectionUtils.isNotEmpty(childDomains)) {
for (DomainVO childDomain : childDomains) {
recalculateDomainResourceCount(childDomain.getId(), type, tag);
}
}
if (CollectionUtils.isNotEmpty(accounts)) {
for (AccountVO account : accounts) {
recalculateAccountResourceCount(account.getId(), type, tag);
}
}
return Transaction.execute((TransactionCallback<Long>) status -> {
long newResourceCount = 0L;
List<Long> domainIdList = childDomains.stream().map(DomainVO::getId).collect(Collectors.toList());
domainIdList.add(domainId);
List<Long> accountIdList = accounts.stream().map(AccountVO::getId).collect(Collectors.toList());
List<ResourceCountVO> domainRCList = _resourceCountDao.findByOwnersAndTypeAndTag(domainIdList, ResourceOwnerType.Domain, type, tag);
List<ResourceCountVO> accountRCList = _resourceCountDao.findByOwnersAndTypeAndTag(accountIdList, ResourceOwnerType.Account, type, tag);
Set<Long> rowIdsToLock = new HashSet<>();
if (domainRCList != null) {
rowIdsToLock.addAll(domainRCList.stream().map(ResourceCountVO::getId).collect(Collectors.toList()));
}
if (accountRCList != null) {
rowIdsToLock.addAll(accountRCList.stream().map(ResourceCountVO::getId).collect(Collectors.toList()));
}
// lock the resource count rows for current domain, immediate child domain & accounts
List<ResourceCountVO> resourceCounts = _resourceCountDao.lockRows(rowIdsToLock);
long oldResourceCount = 0L;
ResourceCountVO domainRC = null;
// calculate project count here
if (type == ResourceType.project) {
newResourceCount += _projectDao.countProjectsForDomain(domainId);
}
for (ResourceCountVO resourceCount : resourceCounts) {
if (resourceCount.getResourceOwnerType() == ResourceOwnerType.Domain && resourceCount.getDomainId() == domainId) {
oldResourceCount = resourceCount.getCount();
domainRC = resourceCount;
} else {
newResourceCount += resourceCount.getCount();
}
}
if (oldResourceCount != newResourceCount) {
domainRC.setCount(newResourceCount);
_resourceCountDao.update(domainRC.getId(), domainRC);
logger.warn("Discrepency in the resource count has been detected " + "(original count = " + oldResourceCount + " correct count = " + newResourceCount + ") for Type = " + type
+ " for Domain ID = " + domainId + " is fixed during resource count recalculation.");
}
return newResourceCount;
});
}
protected void cleanupStaleResourceReservations(final long accountId, final ResourceType type, String tag) {
Long delay = ResourceReservationCleanupDelay.value();
if (delay == null || delay <= 0) {
return;
}
Date cleanupBefore = new Date(System.currentTimeMillis() - delay * 1000);
int rowsRemoved = reservationDao.removeStaleReservations(accountId, type, tag, cleanupBefore);
if (rowsRemoved > 0) {
logger.warn("Removed {} stale resource reservations for account {} of type {} and tag {}",
rowsRemoved, accountId, type, tag);
}
}
@DB
protected long recalculateAccountResourceCount(final long accountId, final ResourceType type, String tag) {
cleanupStaleResourceReservations(accountId, type, tag);
final Long newCount;
if (type == Resource.ResourceType.user_vm) {
newCount = calculateVmCountForAccount(accountId, tag);
} else if (type == Resource.ResourceType.volume) {
newCount = calculateVolumeCountForAccount(accountId, tag);
} else if (type == Resource.ResourceType.snapshot) {
newCount = _snapshotDao.countSnapshotsForAccount(accountId);
} else if (type == Resource.ResourceType.public_ip) {
newCount = calculatePublicIpForAccount(accountId);
} else if (type == Resource.ResourceType.template) {
newCount = _vmTemplateDao.countTemplatesForAccount(accountId);
} else if (type == Resource.ResourceType.project) {
newCount = _projectAccountDao.countByAccountIdAndRole(accountId, Role.Admin);
} else if (type == Resource.ResourceType.network) {
newCount = _networkDao.countNetworksUserCanCreate(accountId);
} else if (type == Resource.ResourceType.vpc) {
newCount = _vpcDao.countByAccountId(accountId);
} else if (type == Resource.ResourceType.cpu) {
newCount = calculateVmCpuCountForAccount(accountId, tag);
} else if (type == Resource.ResourceType.memory) {
newCount = calculateVmMemoryCountForAccount(accountId, tag);
} else if (type == Resource.ResourceType.primary_storage) {
newCount = calculatePrimaryStorageForAccount(accountId, tag);
} else if (type == Resource.ResourceType.secondary_storage) {
newCount = calculateSecondaryStorageForAccount(accountId);
} else {
throw new InvalidParameterValueException("Unsupported resource type " + type);
}
long oldCount = 0;
final ResourceCountVO accountRC = _resourceCountDao.findByOwnerAndTypeAndTag(accountId, ResourceOwnerType.Account, type, tag);
if (accountRC != null) {
oldCount = accountRC.getCount();
if (newCount == null || !newCount.equals(oldCount)) {
accountRC.setCount((newCount == null) ? 0 : newCount);
_resourceCountDao.update(accountRC.getId(), accountRC);
}
} else if (newCount != null) {
_resourceCountDao.persist(new ResourceCountVO(type, newCount, accountId, ResourceOwnerType.Account, tag));
}
// No need to log message for primary and secondary storage because both are recalculating the
// resource count which will not lead to any discrepancy.
if (newCount != null && !newCount.equals(oldCount) &&
type != Resource.ResourceType.primary_storage && type != Resource.ResourceType.secondary_storage) {
logger.warn("Discrepancy in the resource count " + "(original count=" + oldCount + " correct count = " + newCount + ") for type " + type +
" for account ID " + accountId + " is fixed during resource count recalculation.");
}
return (newCount == null) ? 0 : newCount;
}
protected List<UserVmJoinVO> getVmsWithAccountAndTag(long accountId, String tag) {
List<VirtualMachine.State> states = new ArrayList<>(Arrays.asList(State.Destroyed, State.Error, State.Expunging));
if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
states.add(State.Stopped);
}
if (StringUtils.isEmpty(tag)) {
return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states, null, null);
}
List<Long> offerings = serviceOfferingDao.listIdsByHostTag(tag);
List<Long> templates = _vmTemplateDao.listIdsByTemplateTag(tag);
if (CollectionUtils.isEmpty(offerings) && CollectionUtils.isEmpty(templates)) {
return new ArrayList<>();
}
return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states,
offerings, templates);
}
protected List<UserVmJoinVO> getVmsWithAccount(long accountId) {
return getVmsWithAccountAndTag(accountId, null);
}
protected List<VolumeVO> getVolumesWithAccountAndTag(long accountId, String tag) {
List<DiskOfferingVO> offerings = diskOfferingDao.listByStorageTag(tag);
if (CollectionUtils.isEmpty(offerings)) {
return new ArrayList<>();
}
List<Long> vrIds = _vmDao.findIdsOfAllocatedVirtualRoutersForAccount(accountId);
return _volumeDao.listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms(accountId,
offerings.stream().map(DiskOfferingVO::getId).collect(Collectors.toList()),
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);
long reservedVMs = calculateReservedResources(vms, accountId, ResourceType.user_vm, tag);
return vms.size() - reservedVMs;
}
protected long calculateVolumeCountForAccount(long accountId, String tag) {
if (StringUtils.isEmpty(tag)) {
long virtualRouterCount = _vmDao.findIdsOfAllocatedVirtualRoutersForAccount(accountId).size();
return _volumeDao.countAllocatedVolumesForAccount(accountId) - virtualRouterCount; // don't count the volumes of virtual router
}
List<VolumeVO> volumes = getVolumesWithAccountAndTag(accountId, tag);
return volumes.size();
}
protected long calculateVmCpuCountForAccount(long accountId, String tag) {
if (StringUtils.isEmpty(tag)) {
return countCpusForAccount(accountId);
}
long cputotal = 0;
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
for (UserVmJoinVO vm : vms) {
cputotal += vm.getCpu();
}
long reservedCpus = calculateReservedResources(vms, accountId, ResourceType.cpu, tag);
return cputotal - reservedCpus;
}
protected long calculateVmMemoryCountForAccount(long accountId, String tag) {
if (StringUtils.isEmpty(tag)) {
return calculateMemoryForAccount(accountId);
}
long memory = 0;
List<UserVmJoinVO> vms = getVmsWithAccountAndTag(accountId, tag);
for (UserVmJoinVO vm : vms) {
memory += vm.getRamSize();
}
long reservedMemory = calculateReservedResources(vms, accountId, ResourceType.memory, tag);
return memory - reservedMemory;
}
public long countCpusForAccount(long accountId) {
long cputotal = 0;
List<UserVmJoinVO> userVms = getVmsWithAccount(accountId);
for (UserVmJoinVO vm : userVms) {
cputotal += vm.getCpu();
}
long reservedCpuTotal = calculateReservedResources(userVms, accountId, ResourceType.cpu, null);
return cputotal - reservedCpuTotal;
}
public long calculateMemoryForAccount(long accountId) {
long ramtotal = 0;
List<UserVmJoinVO> userVms = getVmsWithAccount(accountId);
for (UserVmJoinVO vm : userVms) {
ramtotal += vm.getRamSize();
}
long reservedRamTotal = calculateReservedResources(userVms, accountId, ResourceType.memory, null);
return ramtotal - reservedRamTotal;
}
public long calculateSecondaryStorageForAccount(long accountId) {
long totalVolumesSize = _volumeDao.secondaryStorageUsedForAccount(accountId);
long totalSnapshotsSize = 0;
long totalTemplatesSize = 0;
SearchCriteria<SumCount> sc = templateSizeSearch.create();
sc.setParameters("downloadState", Status.DOWNLOADED);
sc.setParameters("destroyed", false);
sc.setJoinParameters("templates", "accountId", accountId);
List<SumCount> templates = _vmTemplateStoreDao.customSearch(sc, null);
if (templates != null) {
totalTemplatesSize = templates.get(0).sum;
}
SearchCriteria<SumCount> sc2 = snapshotSizeSearch.create();
sc2.setParameters("state", ObjectInDataStoreStateMachine.State.Ready);
sc2.setParameters("storeRole", DataStoreRole.Image);
sc2.setJoinParameters("snapshots", "accountId", accountId);
List<SumCount> snapshots = _snapshotDataStoreDao.customSearch(sc2, null);
if (snapshots != null) {
totalSnapshotsSize = snapshots.get(0).sum;
}
return totalVolumesSize + totalSnapshotsSize + totalTemplatesSize;
}
private long calculatePublicIpForAccount(long accountId) {
Long dedicatedCount = 0L;
Long allocatedCount = 0L;
List<VlanVO> dedicatedVlans = _vlanDao.listDedicatedVlans(accountId);
for (VlanVO dedicatedVlan : dedicatedVlans) {
List<IPAddressVO> ips = _ipAddressDao.listByVlanId(dedicatedVlan.getId());
dedicatedCount += new Long(ips.size());
}
allocatedCount = _ipAddressDao.countAllocatedIPsForAccount(accountId);
if (dedicatedCount > allocatedCount) {
return dedicatedCount;
} else {
return allocatedCount;
}
}
protected long calculatePrimaryStorageForAccount(long accountId, String tag) {
if (StringUtils.isEmpty(tag)) {
List<Long> virtualRouters = _vmDao.findIdsOfAllocatedVirtualRoutersForAccount(accountId);
return _volumeDao.primaryStorageUsedForAccount(accountId, virtualRouters);
}
long storage = 0;
List<VolumeVO> volumes = getVolumesWithAccountAndTag(accountId, tag);
for (VolumeVO volume : volumes) {
storage += volume.getSize() == null ? 0L : volume.getSize();
}
return storage;
}
@Override
public long getResourceCount(Account account, ResourceType type, String tag) {
return _resourceCountDao.getResourceCount(account.getId(), ResourceOwnerType.Account, type, tag);
}
private boolean isDisplayFlagOn(Boolean displayResource) {
// 1. If its null assume displayResource = 1
// 2. If its not null then send true if displayResource = 1
return ! Boolean.FALSE.equals(displayResource);
}
@Override
public void checkResourceLimit(Account account, ResourceType type, Boolean displayResource, long... count) throws ResourceAllocationException {
if (isDisplayFlagOn(displayResource)) {
checkResourceLimit(account, type, count);
}
}
@Override
public void incrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta) {
if (isDisplayFlagOn(displayResource)) {
incrementResourceCount(accountId, type, delta);
}
}
@Override
public void decrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta) {
if (isDisplayFlagOn(displayResource)) {
decrementResourceCount(accountId, type, delta);
}
}
@Override
public void changeResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta) {
// meaning that the display flag is not changed so neither increment or decrement
if (displayResource == null) {
return;
}
// Increment because the display is turned on.
if (displayResource) {
incrementResourceCount(accountId, type, delta);
} else {
decrementResourceCount(accountId, type, delta);
}
}
@Override
public List<String> getResourceLimitHostTags() {
if (StringUtils.isEmpty(ResourceLimitService.ResourceLimitHostTags.value())) {
return new ArrayList<>();
}
return Stream.of(ResourceLimitService.ResourceLimitHostTags.value().split(","))
.map(String::trim)
.collect(Collectors.toList());
}
@Override
public List<String> getResourceLimitStorageTags() {
if (StringUtils.isEmpty(ResourceLimitService.ResourceLimitStorageTags.value())) {
return new ArrayList<>();
}
return Arrays.asList(ResourceLimitService.ResourceLimitStorageTags.value().split(","));
}
protected TaggedResourceLimitAndCountResponse getTaggedResourceLimitAndCountResponse(Account account,
Domain domain, ResourceOwnerType ownerType, ResourceType type, String tag) {
Long limit = ResourceOwnerType.Account.equals(ownerType) ?
findCorrectResourceLimitForAccount(account, type, tag) :
findCorrectResourceLimitForDomain(domain, type, tag);
Long count = 0L;
ResourceCountVO countVO = _resourceCountDao.findByOwnerAndTypeAndTag(
ResourceOwnerType.Account.equals(ownerType) ? account.getId() : domain.getId(), ownerType, type, tag);
if (countVO != null) {
count = countVO.getCount();
}
TaggedResourceLimitAndCountResponse taggedResourceLimitAndCountResponse = new TaggedResourceLimitAndCountResponse();
taggedResourceLimitAndCountResponse.setResourceType(type);
taggedResourceLimitAndCountResponse.setTag(tag);
taggedResourceLimitAndCountResponse.setLimit(limit);
taggedResourceLimitAndCountResponse.setTotal(count);
taggedResourceLimitAndCountResponse.setAvailable(limit == Resource.RESOURCE_UNLIMITED ? Resource.RESOURCE_UNLIMITED : (limit - count));
return taggedResourceLimitAndCountResponse;
}
protected void updateTaggedResourceLimitsAndCounts(String uuid, ResourceOwnerType ownerType, List<String> hostTags,
List<String> storageTags, ResourceLimitAndCountResponse response) {
Account account = null;
if (ResourceOwnerType.Account.equals(ownerType)) {
account = _accountDao.findByUuid(uuid);
}
Domain domain = null;
if (ResourceOwnerType.Domain.equals(ownerType)) {
domain = _domainDao.findByUuid(uuid);
}
List<TaggedResourceLimitAndCountResponse> taggedResponses = new ArrayList<>();
for (String tag : hostTags) {
for (ResourceType type : HostTagsSupportingTypes) {
taggedResponses.add(getTaggedResourceLimitAndCountResponse(account, domain, ownerType, type, tag));
}
}
for (String tag : storageTags) {
for (ResourceType type : StorageTagsSupportingTypes) {
taggedResponses.add(getTaggedResourceLimitAndCountResponse(account, domain, ownerType, type, tag));
}
}
response.setTaggedResourceLimitsAndCounts(taggedResponses);
}
protected void updateTaggedResourceLimitsAndCountsForAccountsOrDomains(List<AccountResponse> accountResponses, List<DomainResponse> domainResponses, String tag) {
List<String> hostTags = new ArrayList<>(getResourceLimitHostTags());
List<String> storageTags = new ArrayList<>(getResourceLimitStorageTags());
if (StringUtils.isNotEmpty(tag)) {
hostTags.retainAll(List.of(tag));
storageTags.retainAll(List.of(tag));
}
if (CollectionUtils.isEmpty(hostTags) && CollectionUtils.isEmpty(storageTags)) {
return;
}
if (CollectionUtils.isNotEmpty(accountResponses)) {
for (AccountResponse response : accountResponses) {
updateTaggedResourceLimitsAndCounts(response.getObjectId(), ResourceOwnerType.Account, hostTags, storageTags, response);
}
}
if (CollectionUtils.isNotEmpty(domainResponses)) {
for (DomainResponse response : domainResponses) {
updateTaggedResourceLimitsAndCounts(response.getId(), ResourceOwnerType.Domain, hostTags, storageTags, response);
}
}
}
@Override
public void updateTaggedResourceLimitsAndCountsForAccounts(List<AccountResponse> responses, String tag) {
updateTaggedResourceLimitsAndCountsForAccountsOrDomains(responses, null, tag);
}
@Override
public void updateTaggedResourceLimitsAndCountsForDomains(List<DomainResponse> responses, String tag) {
updateTaggedResourceLimitsAndCountsForAccountsOrDomains(null, responses, tag);
}
@Override
public List<String> getResourceLimitHostTags(ServiceOffering serviceOffering, VirtualMachineTemplate template) {
if (StringUtils.isEmpty(serviceOffering.getHostTag()) && StringUtils.isEmpty(template.getTemplateTag())) {
return new ArrayList<>();
}
List<String> resourceLimitTagsFromConfig = getResourceLimitHostTags();
if (CollectionUtils.isEmpty(resourceLimitTagsFromConfig)) {
return new ArrayList<>();
}
List<String> tags = new ArrayList<>();
if (StringUtils.isNotEmpty(serviceOffering.getHostTag())) {
List<String> offeringTags = com.cloud.utils.StringUtils.csvTagsToList(serviceOffering.getHostTag());
for (String tag : offeringTags) {
if (StringUtils.isNotEmpty(tag) && resourceLimitTagsFromConfig.contains(tag)) {
tags.add(tag);
}
}
}
if (StringUtils.isNotEmpty(template.getTemplateTag())
&& resourceLimitTagsFromConfig.contains(template.getTemplateTag())
&& !tags.contains(template.getTemplateTag())) {
tags.add(template.getTemplateTag());
}
return tags;
}
@Override
public List<String> getResourceLimitStorageTags(DiskOffering diskOffering) {
if (diskOffering == null || StringUtils.isEmpty(diskOffering.getTags())) {
return new ArrayList<>();
}
List<String> resourceLimitTagsFromConfig = getResourceLimitStorageTags();
if (CollectionUtils.isEmpty(resourceLimitTagsFromConfig)) {
return new ArrayList<>();
}
String[] offeringTags = diskOffering.getTagsArray();
List<String> tags = new ArrayList<>();
for (String tag : offeringTags) {
if (StringUtils.isNotEmpty(tag) && resourceLimitTagsFromConfig.contains(tag)) {
tags.add(tag);
}
}
return tags;
}
protected List<String> getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering) {
if (Boolean.FALSE.equals(display)) {
return new ArrayList<>();
}
List<String> tags = getResourceLimitStorageTags(diskOffering);
if (tags.isEmpty()) {
tags.add(null);
} else {
tags.add(0, null);
}
return tags;
}
@Override
public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException {
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.volume, tag);
if (size != null) {
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size);
}
}
}
@Override
public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException {
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (size != null) {
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size);
}
}
}
@Override
public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
DiskOffering currentOffering, DiskOffering newOffering
) throws ResourceAllocationException {
Ternary<Set<String>, Set<String>, Set<String>> updatedResourceLimitStorageTags = getResourceLimitStorageTagsForDiskOfferingChange(display, currentOffering, newOffering);
if (updatedResourceLimitStorageTags == null) {
return;
}
Set<String> sameTags = updatedResourceLimitStorageTags.first();
Set<String> newTags = updatedResourceLimitStorageTags.second();
if (newSize > currentSize) {
for (String tag : sameTags) {
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize - currentSize);
}
}
for (String tag : newTags) {
checkResourceLimitWithTag(owner, ResourceType.volume, tag, 1L);
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize);
}
}
@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;
}
for (String tag : tags) {
incrementResourceCountWithTag(accountId, ResourceType.volume, tag);
if (size != null) {
incrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size);
}
}
}
});
}
@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;
}
for (String tag : tags) {
decrementResourceCountWithTag(accountId, ResourceType.volume, tag);
if (size != null) {
decrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size);
}
}
}
});
}
@Override
public void updateVmResourceCountForTemplateChange(long accountId, Boolean display, ServiceOffering offering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate
) {
updateVmResourceCountForServiceOfferingAndTemplateChange(accountId, display, null, null, null, null,
offering, offering, currentTemplate, newTemplate);
}
@Override
public void updateVmResourceCountForServiceOfferingChange(long accountId, Boolean display, Long currentCpu, Long newCpu,Long currentMemory, Long newMemory,
ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template
) {
updateVmResourceCountForServiceOfferingAndTemplateChange(accountId, display, currentCpu, newCpu, currentMemory, newMemory, currentOffering,
newOffering != null ? newOffering : currentOffering, template, template);
}
private Ternary<Set<String>, Set<String>, Set<String>> getResourceLimitHostTagsForVmServiceOfferingAndTemplateChange(
Boolean display, ServiceOffering currentOffering, ServiceOffering newOffering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate
) {
Set<String> currentOfferingTags = new HashSet<>(getResourceLimitHostTagsForResourceCountOperation(display, currentOffering, currentTemplate));
if (currentOffering.getId() == newOffering.getId() && currentTemplate.getId() == newTemplate.getId()) {
return new Ternary<>(currentOfferingTags, new HashSet<>(), new HashSet<>());
}
Set<String> newOfferingTags = new HashSet<>(getResourceLimitHostTagsForResourceCountOperation(display, newOffering, newTemplate));
if (currentOfferingTags.isEmpty() && newOfferingTags.isEmpty()) {
return null;
}
Set<String> sameTags = currentOfferingTags.stream().filter(newOfferingTags::contains).collect(Collectors.toSet());;
Set<String> newTags = newOfferingTags.stream().filter(tag -> !currentOfferingTags.contains(tag)).collect(Collectors.toSet());
Set<String> removedTags = currentOfferingTags.stream().filter(tag -> !newOfferingTags.contains(tag)).collect(Collectors.toSet());
return new Ternary<>(sameTags, newTags, removedTags);
}
private void updateVmResourceCountForServiceOfferingAndTemplateChange(long accountId, Boolean display, Long currentCpu,
Long newCpu, Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate
) {
Ternary<Set<String>, Set<String>, Set<String>> updatedResourceLimitHostTags = getResourceLimitHostTagsForVmServiceOfferingAndTemplateChange(display, currentOffering, newOffering, currentTemplate, newTemplate);
if (updatedResourceLimitHostTags == null) {
return;
}
if (currentCpu == null) {
currentCpu = currentOffering.getCpu() != null ? Long.valueOf(currentOffering.getCpu()) : 0L;
}
if (newCpu == null) {
newCpu = newOffering.getCpu() != null ? Long.valueOf(newOffering.getCpu()) : 0L;
}
if (currentMemory == null) {
currentMemory = currentOffering.getRamSize() != null ? Long.valueOf(currentOffering.getRamSize()) : 0L;
}
if (newMemory == null) {
newMemory = newOffering.getRamSize() != null ? Long.valueOf(newOffering.getRamSize()) : 0L;
}
Set<String> sameTags = updatedResourceLimitHostTags.first();
Set<String> newTags = updatedResourceLimitHostTags.second();
Set<String> removedTags = updatedResourceLimitHostTags.third();
if (!newCpu.equals(currentCpu) || !newMemory.equals(currentMemory)) {
for (String tag : sameTags) {
if (newCpu - currentCpu > 0) {
incrementResourceCountWithTag(accountId, ResourceType.cpu, tag, newCpu - currentCpu);
} else if (newCpu - currentCpu < 0) {
decrementResourceCountWithTag(accountId, ResourceType.cpu, tag, currentCpu - newCpu);
}
if (newMemory - currentMemory > 0) {
incrementResourceCountWithTag(accountId, ResourceType.memory, tag, newMemory - currentMemory);
} else if (newMemory - currentMemory < 0) {
decrementResourceCountWithTag(accountId, ResourceType.memory, tag, currentMemory - newMemory);
}
}
}
for (String tag : removedTags) {
decrementResourceCountWithTag(accountId, ResourceType.user_vm, tag, 1L);
decrementResourceCountWithTag(accountId, ResourceType.cpu, tag, currentCpu);
decrementResourceCountWithTag(accountId, ResourceType.memory, tag, currentMemory);
}
for (String tag : newTags) {
incrementResourceCountWithTag(accountId, ResourceType.user_vm, tag, 1L);
incrementResourceCountWithTag(accountId, ResourceType.cpu, tag, newCpu);
incrementResourceCountWithTag(accountId, ResourceType.memory, tag, newMemory);
}
}
private Ternary<Set<String>, Set<String>, Set<String>> getResourceLimitStorageTagsForDiskOfferingChange(
Boolean display, DiskOffering currentOffering, DiskOffering newOffering
) {
Set<String> currentOfferingTags = new HashSet<>(getResourceLimitStorageTagsForResourceCountOperation(display, currentOffering));
if (newOffering == null || currentOffering.getId() == newOffering.getId()) {
return new Ternary<>(currentOfferingTags, new HashSet<>(), new HashSet<>());
}
Set<String> newOfferingTags = new HashSet<>(getResourceLimitStorageTagsForResourceCountOperation(display, newOffering));
if (currentOfferingTags.isEmpty() && newOfferingTags.isEmpty()) {
return null;
}
Set<String> sameTags = currentOfferingTags.stream().filter(newOfferingTags::contains).collect(Collectors.toSet());;
Set<String> newTags = newOfferingTags.stream().filter(tag -> !currentOfferingTags.contains(tag)).collect(Collectors.toSet());
Set<String> removedTags = currentOfferingTags.stream().filter(tag -> !newOfferingTags.contains(tag)).collect(Collectors.toSet());
return new Ternary<>(sameTags, newTags, removedTags);
}
@Override
public void updateVolumeResourceCountForDiskOfferingChange(long accountId, Boolean display, Long currentSize, Long newSize,
DiskOffering currentOffering, DiskOffering newOffering
) {
Ternary<Set<String>, Set<String>, Set<String>> updatedResourceLimitStorageTags = getResourceLimitStorageTagsForDiskOfferingChange(display, currentOffering, newOffering);
if (updatedResourceLimitStorageTags == null) {
return;
}
Set<String> sameTags = updatedResourceLimitStorageTags.first();
Set<String> newTags = updatedResourceLimitStorageTags.second();
Set<String> removedTags = updatedResourceLimitStorageTags.third();
if (!newSize.equals(currentSize)) {
for (String tag : sameTags) {
if (newSize - currentSize > 0) {
incrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, newSize - currentSize);
} else if (newSize - currentSize < 0) {
decrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, currentSize - newSize);
}
}
}
for (String tag : removedTags) {
decrementResourceCountWithTag(accountId, ResourceType.volume, tag, 1L);
decrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, currentSize);
}
for (String tag : newTags) {
incrementResourceCountWithTag(accountId, ResourceType.volume, tag, 1L);
incrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, newSize);
}
}
@Override
public void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) {
if (size == null) {
return;
}
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
}
for (String tag : tags) {
incrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size);
}
}
@Override
public void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) {
if (size == null) {
return;
}
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
}
for (String tag : tags) {
decrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size);
}
}
protected List<String> getResourceLimitHostTagsForResourceCountOperation(Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) {
if (Boolean.FALSE.equals(display)) {
return new ArrayList<>();
}
List<String> tags = getResourceLimitHostTags(serviceOffering, template);
if (tags.isEmpty()) {
tags.add(null);
} else {
tags.add(0, null);
}
return tags;
}
@Override
public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.user_vm, tag);
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu);
checkResourceLimitWithTag(owner, ResourceType.memory, tag, ram);
}
}
@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;
}
Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
for (String tag : tags) {
incrementResourceCountWithTag(accountId, ResourceType.user_vm, tag);
incrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu);
incrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram);
}
}
});
}
@Override
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;
}
Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
for (String tag : tags) {
decrementResourceCountWithTag(accountId, ResourceType.user_vm, tag);
decrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu);
decrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram);
}
}
});
}
@Override
public void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException {
checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, null, null,
null, null, offering, offering, currentTemplate, newTemplate);
}
@Override
public void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
Long currentMemory, Long newMemory,
ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template
) throws ResourceAllocationException {
checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, currentCpu, newCpu, currentMemory, newMemory, currentOffering,
newOffering != null ? newOffering : currentOffering, template, template);
}
private void checkVmResourceLimitsForServiceOfferingAndTemplateChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate
) throws ResourceAllocationException {
Ternary<Set<String>, Set<String>, Set<String>> updatedResourceLimitHostTags = getResourceLimitHostTagsForVmServiceOfferingAndTemplateChange(display, currentOffering, newOffering, currentTemplate, newTemplate);
if (updatedResourceLimitHostTags == null) {
return;
}
if (currentCpu == null) {
currentCpu = currentOffering.getCpu() != null ? Long.valueOf(currentOffering.getCpu()) : 0L;
}
if (newCpu == null) {
newCpu = newOffering.getCpu() != null ? Long.valueOf(newOffering.getCpu()) : 0L;
}
if (currentMemory == null) {
currentMemory = currentOffering.getRamSize() != null ? Long.valueOf(currentOffering.getRamSize()) : 0L;
}
if (newMemory == null) {
newMemory = newOffering.getRamSize() != null ? Long.valueOf(newOffering.getRamSize()) : 0L;
}
Set<String> sameTags = updatedResourceLimitHostTags.first();
Set<String> newTags = updatedResourceLimitHostTags.second();
if (newCpu - currentCpu > 0 || newMemory - currentMemory > 0) {
for (String tag : sameTags) {
if (newCpu - currentCpu > 0) {
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu - currentCpu);
}
if (newMemory - currentMemory > 0) {
checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory - currentMemory);
}
}
}
for (String tag : newTags) {
checkResourceLimitWithTag(owner, ResourceType.user_vm, tag, 1L);
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu);
checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory);
}
}
@Override
public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (cpu == null) {
cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu);
}
}
@Override
public void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (cpu == null) {
cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
}
for (String tag : tags) {
incrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu);
}
}
@Override
public void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (cpu == null) {
cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
}
for (String tag : tags) {
decrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu);
}
}
@Override
public void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (memory == null) {
memory = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.memory, tag, memory);
}
}
@Override
public void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (memory == null) {
memory = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
}
for (String tag : tags) {
incrementResourceCountWithTag(accountId, ResourceType.memory, tag, memory);
}
}
@Override
public void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (memory == null) {
memory = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
}
for (String tag : tags) {
decrementResourceCountWithTag(accountId, ResourceType.memory, tag, memory);
}
}
@Override
public String getConfigComponentName() {
return ResourceLimitManagerImpl.class.getName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {
ResourceCountCheckInterval,
ResourceReservationCleanupDelay,
MaxAccountSecondaryStorage,
MaxProjectSecondaryStorage,
ResourceLimitHostTags,
ResourceLimitStorageTags,
DefaultMaxAccountProjects,
DefaultMaxDomainProjects
};
}
protected class ResourceCountCheckTask extends ManagedContextRunnable {
public ResourceCountCheckTask() {
}
@Override
protected void runInContext() {
GlobalLock lock = GlobalLock.getInternLock("ResourceCheckTask");
try {
if (lock.lock(30)) {
try {
ManagementServerHostVO msHost = managementServerHostDao.findOneByLongestRuntime();
if (msHost == null || (msHost.getMsid() != ManagementServerNode.getManagementServerId())) {
logger.trace("Skipping the resource counters recalculation task on this management server");
return;
}
runResourceCheckTaskInternal();
} finally {
lock.unlock();
}
}
} finally {
lock.releaseRef();
}
}
private void runResourceCheckTaskInternal() {
logger.info("Started resource counters recalculation periodic task.");
List<DomainVO> domains;
List<AccountVO> accounts;
// try/catch task, otherwise it won't be rescheduled in case of exception
try {
domains = _domainDao.findImmediateChildrenForParent(Domain.ROOT_DOMAIN);
} catch (Exception e) {
logger.warn("Resource counters recalculation periodic task failed, unable to fetch immediate children for the domain " + Domain.ROOT_DOMAIN, e);
// initialize domains as empty list to do best effort recalculation
domains = new ArrayList<>();
}
// try/catch task, otherwise it won't be rescheduled in case of exception
try {
accounts = _accountDao.findActiveAccountsForDomain(Domain.ROOT_DOMAIN);
} catch (Exception e) {
logger.warn("Resource counters recalculation periodic task failed, unable to fetch active accounts for domain " + Domain.ROOT_DOMAIN, e);
// initialize accounts as empty list to do best effort recalculation
accounts = new ArrayList<>();
}
// try/catch task, otherwise it won't be rescheduled in case of exception
try {
removeResourceLimitAndCountForNonMatchingTags(null, null, getResourceLimitHostTags(), getResourceLimitStorageTags());
} catch (Exception e) {
logger.warn("Failure in resource counters recalculation periodic task, unable to clear undesired tagged limits and counts", e);
}
for (ResourceType type : ResourceType.values()) {
if (CollectionUtils.isEmpty(domains)) {
recalculateDomainResourceCount(Domain.ROOT_DOMAIN, type, null);
recalculateDomainTaggedResourceCount(Domain.ROOT_DOMAIN, type, getResourceLimitHostTags(), getResourceLimitStorageTags());
} else {
for (Domain domain : domains) {
recalculateDomainResourceCount(domain.getId(), type, null);
recalculateDomainTaggedResourceCount(domain.getId(), type, getResourceLimitHostTags(), getResourceLimitStorageTags());
}
}
// run through the accounts in the root domain
for (AccountVO account : accounts) {
recalculateAccountResourceCount(account.getId(), type, null);
recalculateAccountTaggedResourceCount(account.getId(), type, getResourceLimitHostTags(), getResourceLimitStorageTags());
}
}
logger.info("Finished resource counters recalculation periodic task.");
}
}
}