blob: b17d27b0adfdecb43ee8eb6c5e23fac0e5ded963 [file] [log] [blame]
//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 org.apache.cloudstack.quota;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.user.Account;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.usage.UsageUnitTypes;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.usage.UsageUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
@Component
public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
private static final Logger s_logger = Logger.getLogger(QuotaManagerImpl.class.getName());
@Inject
private AccountDao _accountDao;
@Inject
private QuotaAccountDao _quotaAcc;
@Inject
private UsageDao _usageDao;
@Inject
private QuotaTariffDao _quotaTariffDao;
@Inject
private QuotaUsageDao _quotaUsageDao;
@Inject
private QuotaBalanceDao _quotaBalanceDao;
@Inject
private ConfigurationDao _configDao;
@Inject
protected PresetVariableHelper presetVariableHelper;
private TimeZone _usageTimezone;
private int _aggregationDuration = 0;
static final BigDecimal s_hoursInMonth = BigDecimal.valueOf(DateUtil.HOURS_IN_A_MONTH);
static final BigDecimal GiB_DECIMAL = BigDecimal.valueOf(ByteScaleUtils.GiB);
List<Account.Type> lockablesAccountTypes = Arrays.asList(Account.Type.NORMAL, Account.Type.DOMAIN_ADMIN);
public QuotaManagerImpl() {
super();
}
private void mergeConfigs(Map<String, String> dbParams, Map<String, Object> xmlParams) {
for (Map.Entry<String, Object> param : xmlParams.entrySet()) {
dbParams.put(param.getKey(), (String)param.getValue());
}
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
super.configure(name, params);
Map<String, String> configs = _configDao.getConfiguration(params);
if (params != null) {
mergeConfigs(configs, params);
}
String aggregationRange = configs.get("usage.stats.job.aggregation.range");
String timeZoneStr = configs.get("usage.aggregation.timezone");
if (timeZoneStr == null) {
timeZoneStr = "GMT";
}
_usageTimezone = TimeZone.getTimeZone(timeZoneStr);
_aggregationDuration = Integer.parseInt(aggregationRange);
if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) {
s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN);
_aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN;
}
s_logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration);
return true;
}
@Override
public boolean start() {
if (s_logger.isInfoEnabled()) {
s_logger.info("Starting Quota Manager");
}
return true;
}
@Override
public boolean stop() {
if (s_logger.isInfoEnabled()) {
s_logger.info("Stopping Quota Manager");
}
return true;
}
protected void processQuotaBalanceForAccount(AccountVO accountVo, List<QuotaUsageVO> accountQuotaUsages) {
String accountToString = accountVo.reflectionToString();
if (CollectionUtils.isEmpty(accountQuotaUsages)) {
s_logger.info(String.format("Account [%s] does not have quota usages to process. Skipping it.", accountToString));
return;
}
QuotaUsageVO firstQuotaUsage = accountQuotaUsages.get(0);
Date startDate = firstQuotaUsage.getStartDate();
Date endDate = firstQuotaUsage.getStartDate();
s_logger.info(String.format("Processing quota balance for account [%s] between [%s] and [%s].", accountToString, startDate,
accountQuotaUsages.get(accountQuotaUsages.size() - 1).getEndDate()));
BigDecimal aggregatedUsage = BigDecimal.ZERO;
long accountId = accountVo.getAccountId();
long domainId = accountVo.getDomainId();
aggregatedUsage = getUsageValueAccordingToLastQuotaUsageEntryAndLastQuotaBalance(accountId, domainId, startDate, endDate, aggregatedUsage, accountToString);
for (QuotaUsageVO quotaUsage : accountQuotaUsages) {
Date quotaUsageStartDate = quotaUsage.getStartDate();
Date quotaUsageEndDate = quotaUsage.getEndDate();
BigDecimal quotaUsed = quotaUsage.getQuotaUsed();
if (quotaUsed.equals(BigDecimal.ZERO)) {
aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, quotaUsageStartDate, quotaUsageEndDate, accountToString));
continue;
}
if (startDate.compareTo(quotaUsageStartDate) == 0) {
aggregatedUsage = aggregatedUsage.subtract(quotaUsed);
continue;
}
_quotaBalanceDao.saveQuotaBalance(new QuotaBalanceVO(accountId, domainId, aggregatedUsage, endDate));
aggregatedUsage = BigDecimal.ZERO;
startDate = quotaUsageStartDate;
endDate = quotaUsageEndDate;
QuotaBalanceVO lastRealBalanceEntry = _quotaBalanceDao.findLastBalanceEntry(accountId, domainId, endDate);
Date lastBalanceDate = new Date(0);
if (lastRealBalanceEntry != null) {
lastBalanceDate = lastRealBalanceEntry.getUpdatedOn();
aggregatedUsage = aggregatedUsage.add(lastRealBalanceEntry.getCreditBalance());
}
aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, lastBalanceDate, endDate, accountToString));
aggregatedUsage = aggregatedUsage.subtract(quotaUsed);
}
_quotaBalanceDao.saveQuotaBalance(new QuotaBalanceVO(accountId, domainId, aggregatedUsage, endDate));
saveQuotaAccount(accountId, aggregatedUsage, endDate);
}
protected BigDecimal getUsageValueAccordingToLastQuotaUsageEntryAndLastQuotaBalance(long accountId, long domainId, Date startDate, Date endDate, BigDecimal aggregatedUsage,
String accountToString) {
QuotaUsageVO lastQuotaUsage = _quotaUsageDao.findLastQuotaUsageEntry(accountId, domainId, startDate);
if (lastQuotaUsage == null) {
aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, new Date(0), startDate, accountToString));
QuotaBalanceVO firstBalance = new QuotaBalanceVO(accountId, domainId, aggregatedUsage, startDate);
s_logger.debug(String.format("Persisting the first quota balance [%s] for account [%s].", firstBalance, accountToString));
_quotaBalanceDao.saveQuotaBalance(firstBalance);
} else {
QuotaBalanceVO lastRealBalance = _quotaBalanceDao.findLastBalanceEntry(accountId, domainId, endDate);
if (lastRealBalance != null) {
aggregatedUsage = aggregatedUsage.add(lastRealBalance.getCreditBalance());
aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, lastRealBalance.getUpdatedOn(), endDate, accountToString));
} else {
s_logger.warn(String.format("Account [%s] has quota usage entries, however it does not have a quota balance.", accountToString));
}
}
return aggregatedUsage;
}
protected void saveQuotaAccount(long accountId, BigDecimal aggregatedUsage, Date endDate) {
QuotaAccountVO quotaAccount = _quotaAcc.findByIdQuotaAccount(accountId);
if (quotaAccount != null) {
quotaAccount.setQuotaBalance(aggregatedUsage);
quotaAccount.setQuotaBalanceDate(endDate);
_quotaAcc.updateQuotaAccount(accountId, quotaAccount);
return;
}
quotaAccount = new QuotaAccountVO(accountId);
quotaAccount.setQuotaBalance(aggregatedUsage);
quotaAccount.setQuotaBalanceDate(endDate);
_quotaAcc.persistQuotaAccount(quotaAccount);
}
protected BigDecimal aggregateCreditBetweenDates(Long accountId, Long domainId, Date startDate, Date endDate, String accountToString) {
List<QuotaBalanceVO> creditsReceived = _quotaBalanceDao.findCreditBalance(accountId, domainId, startDate, endDate);
s_logger.debug(String.format("Account [%s] has [%s] credit entries before [%s].", accountToString, creditsReceived.size(), endDate));
BigDecimal aggregatedUsage = BigDecimal.ZERO;
s_logger.debug(String.format("Aggregating the account [%s] credit entries before [%s].", accountToString, endDate));
for (QuotaBalanceVO credit : creditsReceived) {
aggregatedUsage = aggregatedUsage.add(credit.getCreditBalance());
}
s_logger.debug(String.format("The aggregation of the account [%s] credit entries before [%s] resulted in the value [%s].", accountToString, endDate, aggregatedUsage));
return aggregatedUsage;
}
@Override
public boolean calculateQuotaUsage() {
List<AccountVO> accounts = _accountDao.listAll();
String accountsToString = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(accounts, "id", "uuid", "accountName", "domainId");
s_logger.info(String.format("Starting quota usage calculation for accounts [%s].", accountsToString));
Map<Integer, Pair<List<QuotaTariffVO>, Boolean>> mapQuotaTariffsPerUsageType = createMapQuotaTariffsPerUsageType();
for (AccountVO account : accounts) {
List<UsageVO> usageRecords = getPendingUsageRecordsForQuotaAggregation(account);
if (usageRecords == null) {
s_logger.debug(String.format("Account [%s] does not have pending usage records. Skipping to next account.", account.reflectionToString()));
continue;
}
List<QuotaUsageVO> quotaUsages = createQuotaUsagesAccordingToQuotaTariffs(account, usageRecords, mapQuotaTariffsPerUsageType);
processQuotaBalanceForAccount(account, quotaUsages);
}
s_logger.info(String.format("Finished quota usage calculation for accounts [%s].", accountsToString));
return true;
}
protected List<UsageVO> getPendingUsageRecordsForQuotaAggregation(AccountVO account) {
Long accountId = account.getId();
Long domainId = account.getDomainId();
Pair<List<UsageVO>, Integer> usageRecords = _usageDao.listUsageRecordsPendingForQuotaAggregation(accountId, domainId);
List<UsageVO> records = usageRecords.first();
if (CollectionUtils.isEmpty(records)) {
return null;
}
s_logger.debug(String.format("Retrieved [%s] pending usage records for account [%s].", usageRecords.second(), account.reflectionToString()));
return records;
}
protected List<QuotaUsageVO> createQuotaUsagesAccordingToQuotaTariffs(AccountVO account, List<UsageVO> usageRecords,
Map<Integer, Pair<List<QuotaTariffVO>, Boolean>> mapQuotaTariffsPerUsageType) {
String accountToString = account.reflectionToString();
s_logger.info(String.format("Calculating quota usage of [%s] usage records for account [%s].", usageRecords.size(), accountToString));
List<Pair<UsageVO, QuotaUsageVO>> pairsUsageAndQuotaUsage = new ArrayList<>();
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
for (UsageVO usageRecord : usageRecords) {
int usageType = usageRecord.getUsageType();
if (Boolean.FALSE.equals(shouldCalculateUsageRecord(account,usageRecord))) {
pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null));
continue;
}
Pair<List<QuotaTariffVO>, Boolean> pairQuotaTariffsPerUsageTypeAndHasActivationRule = mapQuotaTariffsPerUsageType.get(usageType);
List<QuotaTariffVO> quotaTariffs = pairQuotaTariffsPerUsageTypeAndHasActivationRule.first();
boolean hasAnyQuotaTariffWithActivationRule = pairQuotaTariffsPerUsageTypeAndHasActivationRule.second();
BigDecimal aggregatedQuotaTariffsValue = aggregateQuotaTariffsValues(usageRecord, quotaTariffs, hasAnyQuotaTariffWithActivationRule, jsInterpreter, accountToString);
QuotaUsageVO quotaUsage = createQuotaUsageAccordingToUsageUnit(usageRecord, aggregatedQuotaTariffsValue, accountToString);
pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, quotaUsage));
}
} catch (Exception e) {
s_logger.error(String.format("Failed to calculate the quota usage for account [%s] due to [%s].", accountToString, e.getMessage()), e);
return new ArrayList<>();
}
return persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(pairsUsageAndQuotaUsage);
}
protected boolean shouldCalculateUsageRecord(AccountVO accountVO, UsageVO usageRecord) {
if (Boolean.FALSE.equals(QuotaConfig.QuotaAccountEnabled.valueIn(accountVO.getAccountId()))) {
s_logger.debug(String.format("Considering usage record [%s] as calculated and skipping it because account [%s] has the quota plugin disabled.",
usageRecord, accountVO.reflectionToString()));
return false;
}
return true;
}
protected List<QuotaUsageVO> persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(List<Pair<UsageVO, QuotaUsageVO>> pairsUsageAndQuotaUsage) {
List<QuotaUsageVO> quotaUsages = new ArrayList<>();
for (Pair<UsageVO, QuotaUsageVO> pairUsageAndQuotaUsage : pairsUsageAndQuotaUsage) {
UsageVO usageVo = pairUsageAndQuotaUsage.first();
usageVo.setQuotaCalculated(1);
_usageDao.persistUsage(usageVo);
QuotaUsageVO quotaUsageVo = pairUsageAndQuotaUsage.second();
if (quotaUsageVo != null) {
_quotaUsageDao.persistQuotaUsage(quotaUsageVo);
quotaUsages.add(quotaUsageVo);
}
}
return quotaUsages;
}
protected BigDecimal aggregateQuotaTariffsValues(UsageVO usageRecord, List<QuotaTariffVO> quotaTariffs, boolean hasAnyQuotaTariffWithActivationRule,
JsInterpreter jsInterpreter, String accountToString) {
String usageRecordToString = usageRecord.toString();
s_logger.debug(String.format("Validating usage record [%s] for account [%s] against [%s] quota tariffs.", usageRecordToString, accountToString,
quotaTariffs.size()));
PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord);
BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO;
for (QuotaTariffVO quotaTariff : quotaTariffs) {
if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) {
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables));
}
}
s_logger.debug(String.format("The aggregation of the quota tariffs resulted in the value [%s] for the usage record [%s]. We will use this value to calculate the final"
+ " usage value.", aggregatedQuotaTariffsValue, usageRecordToString));
return aggregatedQuotaTariffsValue;
}
protected PresetVariables getPresetVariables(boolean hasAnyQuotaTariffWithActivationRule, UsageVO usageRecord) {
if (hasAnyQuotaTariffWithActivationRule) {
return presetVariableHelper.getPresetVariables(usageRecord);
}
return null;
}
/**
* Returns the quota tariff value according to the result of the activation rule.<br/>
* <ul>
* <li>If the activation rule is null or empty, returns {@link QuotaTariffVO#getCurrencyValue()}.</li>
* <li>If the activation rule result in a number, returns it.</li>
* <li>If the activation rule result in a boolean and its is true, returns {@link QuotaTariffVO#getCurrencyValue()}.</li>
* <li>If the activation rule result in a boolean and its is false, returns {@link BigDecimal#ZERO}.</li>
* <li>If the activation rule result in something else, returns {@link BigDecimal#ZERO}.</li>
* </ul>
*/
protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables) {
String activationRule = quotaTariff.getActivationRule();
BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue();
String quotaTariffToString = quotaTariff.toString();
if (StringUtils.isEmpty(activationRule)) {
s_logger.debug(String.format("Quota tariff [%s] does not have an activation rule, therefore we will use the quota tariff value [%s] in the calculation.",
quotaTariffToString, quotaTariffValue));
return quotaTariffValue;
}
injectPresetVariablesIntoJsInterpreter(jsInterpreter, presetVariables);
String scriptResult = jsInterpreter.executeScript(activationRule).toString();
if (NumberUtils.isParsable(scriptResult)) {
s_logger.debug(String.format("The script [%s] of quota tariff [%s] had a numeric value [%s], therefore we will use it in the calculation.", activationRule,
quotaTariffToString, scriptResult));
return new BigDecimal(scriptResult);
}
if (BooleanUtils.toBoolean(scriptResult)) {
s_logger.debug(String.format("The script [%s] of quota tariff [%s] had a true boolean result, therefore we will use the quota tariff's value [%s] in the calculation.",
activationRule, quotaTariffToString, quotaTariffValue));
return quotaTariffValue;
}
s_logger.debug(String.format("The script [%s] of quota tariff [%s] had the result [%s], therefore we will not use this quota tariff in the calculation.", activationRule,
quotaTariffToString, quotaTariffValue));
return BigDecimal.ZERO;
}
/**
* Injects the preset variables into the JS interpreter.
*/
protected void injectPresetVariablesIntoJsInterpreter(JsInterpreter jsInterpreter, PresetVariables presetVariables) {
jsInterpreter.discardCurrentVariables();
jsInterpreter.injectVariable("account", presetVariables.getAccount().toString());
jsInterpreter.injectVariable("domain", presetVariables.getDomain().toString());
GenericPresetVariable project = presetVariables.getProject();
if (project != null) {
jsInterpreter.injectVariable("project", project.toString());
}
jsInterpreter.injectVariable("resourceType", presetVariables.getResourceType());
jsInterpreter.injectVariable("value", presetVariables.getValue().toString());
jsInterpreter.injectVariable("zone", presetVariables.getZone().toString());
}
/**
* Verifies if the quota tariff should be applied on the usage record according to their respectively start and end date.<br/><br/>
*/
protected boolean isQuotaTariffInPeriodToBeApplied(UsageVO usageRecord, QuotaTariffVO quotaTariff, String accountToString) {
Date usageRecordStartDate = usageRecord.getStartDate();
Date usageRecordEndDate = usageRecord.getEndDate();
Date quotaTariffStartDate = quotaTariff.getEffectiveOn();
Date quotaTariffEndDate = quotaTariff.getEndDate();
if ((quotaTariffEndDate != null && usageRecordStartDate.after(quotaTariffEndDate)) || usageRecordEndDate.before(quotaTariffStartDate)) {
s_logger.debug(String.format("Not applying quota tariff [%s] in usage record [%s] of account [%s] due to it is out of the period to be applied. Period of the usage"
+ " record [startDate: %s, endDate: %s], period of the quota tariff [startDate: %s, endDate: %s].", quotaTariff, usageRecord.toString(), accountToString,
DateUtil.getOutputString(usageRecordStartDate), DateUtil.getOutputString(usageRecordEndDate), DateUtil.getOutputString(quotaTariffStartDate),
DateUtil.getOutputString(quotaTariffEndDate)));
return false;
}
return true;
}
protected Map<Integer, Pair<List<QuotaTariffVO>, Boolean>> createMapQuotaTariffsPerUsageType() {
List<QuotaTariffVO> quotaTariffs = _quotaTariffDao.listQuotaTariffs(null, null, null, null, null, false, null, null).first();
Map<Integer, Pair<List<QuotaTariffVO>, Boolean>> mapQuotaTariffsPerUsageType = new HashMap<>();
for (Map.Entry<Integer, QuotaTypes> entry : QuotaTypes.listQuotaTypes().entrySet()) {
int quotaType = entry.getKey();
List<QuotaTariffVO> quotaTariffsFiltered = quotaTariffs.stream().filter(quotaTariff -> quotaTariff.getUsageType() == quotaType).collect(Collectors.toList());
Boolean hasAnyQuotaTariffWithActivationRule = quotaTariffsFiltered.stream().anyMatch(quotaTariff -> StringUtils.isNotEmpty(quotaTariff.getActivationRule()));
mapQuotaTariffsPerUsageType.put(quotaType, new Pair<>(quotaTariffsFiltered, hasAnyQuotaTariffWithActivationRule));
}
return mapQuotaTariffsPerUsageType;
}
protected QuotaUsageVO createQuotaUsageAccordingToUsageUnit(UsageVO usageRecord, BigDecimal aggregatedQuotaTariffsValue, String accountToString) {
String usageRecordToString = usageRecord.toString();
if (aggregatedQuotaTariffsValue.equals(BigDecimal.ZERO)) {
s_logger.debug(String.format("Usage record [%s] for account [%s] does not have quota tariffs to be calculated, therefore we will mark it as calculated.",
usageRecordToString, accountToString));
return null;
}
QuotaTypes quotaType = QuotaTypes.listQuotaTypes().get(usageRecord.getUsageType());
String quotaUnit = quotaType.getQuotaUnit();
s_logger.debug(String.format("Calculating value of usage record [%s] for account [%s] according to the aggregated quota tariffs value [%s] and its usage unit [%s].",
usageRecordToString, accountToString, aggregatedQuotaTariffsValue, quotaUnit));
BigDecimal usageValue = getUsageValueAccordingToUsageUnitType(usageRecord, aggregatedQuotaTariffsValue, quotaUnit);
s_logger.debug(String.format("The calculation of the usage record [%s] for account [%s] according to the aggregated quota tariffs value [%s] and its usage unit [%s] "
+ "resulted in the value [%s].", usageRecordToString, accountToString, aggregatedQuotaTariffsValue, quotaUnit, usageValue));
QuotaUsageVO quotaUsageVo = new QuotaUsageVO();
quotaUsageVo.setUsageItemId(usageRecord.getId());
quotaUsageVo.setZoneId(usageRecord.getZoneId());
quotaUsageVo.setAccountId(usageRecord.getAccountId());
quotaUsageVo.setDomainId(usageRecord.getDomainId());
quotaUsageVo.setUsageType(quotaType.getQuotaType());
quotaUsageVo.setQuotaUsed(usageValue);
quotaUsageVo.setStartDate(usageRecord.getStartDate());
quotaUsageVo.setEndDate(usageRecord.getEndDate());
return quotaUsageVo;
}
protected BigDecimal getUsageValueAccordingToUsageUnitType(UsageVO usageRecord, BigDecimal aggregatedQuotaTariffsValue, String quotaUnit) {
BigDecimal rawUsage = BigDecimal.valueOf(usageRecord.getRawUsage());
BigDecimal costPerHour = aggregatedQuotaTariffsValue.divide(s_hoursInMonth, 8, RoundingMode.HALF_EVEN);
switch (UsageUnitTypes.getByDescription(quotaUnit)) {
case COMPUTE_MONTH:
case IP_MONTH:
case POLICY_MONTH:
return rawUsage.multiply(costPerHour);
case GB:
BigDecimal rawUsageInGb = rawUsage.divide(GiB_DECIMAL, 8, RoundingMode.HALF_EVEN);
return rawUsageInGb.multiply(aggregatedQuotaTariffsValue);
case GB_MONTH:
BigDecimal gbInUse = BigDecimal.valueOf(usageRecord.getSize()).divide(GiB_DECIMAL, 8, RoundingMode.HALF_EVEN);
return rawUsage.multiply(costPerHour).multiply(gbInUse);
case BYTES:
case IOPS:
return rawUsage.multiply(aggregatedQuotaTariffsValue);
default:
return BigDecimal.ZERO;
}
}
@Override
public boolean isLockable(AccountVO account) {
return lockablesAccountTypes.contains(account.getType());
}
}