CLOUDSTACK-8592: optimization and fixes

1. Process usage entries that have greater than 0 usage
    2. Process quota entries only if tariff is non zero
    3. If there are credit entries but no balance entry create a dummy
    balance entry
    4. Remove any credit entries that are before the last balance entry
    when displaying balance statement
    5. Bug: write the balance entry, boundary condition
This commit is contained in:
Abhinandan Prateek 2015-12-05 17:37:11 +05:30
parent 517baf954b
commit 0151cda7dd
8 changed files with 130 additions and 38 deletions

View File

@ -1399,7 +1399,7 @@ label.quota.configuration=Quota Configuration
label.quota.configure=Configure Quota
label.quota.remove=Remove Quota
label.quota.totalusage=Total Usage
label.quota.balance=Balance
label.quota.balance=Free Credits
label.quota.minbalance=Min Balance
label.quota.enforcequota=Enforce Quota
label.quota.summary=Summary

View File

@ -477,6 +477,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
}
qb.and(qb.entity().getQuotaCalculated(), SearchCriteria.Op.NEQ, 1);
qb.and(qb.entity().getRawUsage(), SearchCriteria.Op.GT, 0);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
}

View File

@ -143,17 +143,26 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
switch (usageRecord.getUsageType()) {
case QuotaTypes.RUNNING_VM:
quotaListForAccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
List<QuotaUsageVO> lq = updateQuotaRunningVMUsage(usageRecord, aggregationRatio);
if (!lq.isEmpty()) {
quotaListForAccount.addAll(lq);
}
break;
case QuotaTypes.ALLOCATED_VM:
quotaListForAccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
QuotaUsageVO qu = updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio);
if (qu != null) {
quotaListForAccount.add(qu);
}
break;
case QuotaTypes.SNAPSHOT:
case QuotaTypes.TEMPLATE:
case QuotaTypes.ISO:
case QuotaTypes.VOLUME:
case QuotaTypes.VM_SNAPSHOT:
quotaListForAccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, usageRecord.getUsageType()));
qu = updateQuotaDiskUsage(usageRecord, aggregationRatio, usageRecord.getUsageType());
if (qu != null) {
quotaListForAccount.add(qu);
}
break;
case QuotaTypes.LOAD_BALANCER_POLICY:
case QuotaTypes.PORT_FORWARDING_RULE:
@ -161,11 +170,17 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
case QuotaTypes.NETWORK_OFFERING:
case QuotaTypes.SECURITY_GROUP:
case QuotaTypes.VPN_USERS:
quotaListForAccount.add(updateQuotaRaw(usageRecord, aggregationRatio, usageRecord.getUsageType()));
qu = updateQuotaRaw(usageRecord, aggregationRatio, usageRecord.getUsageType());
if (qu != null) {
quotaListForAccount.add(qu);
}
break;
case QuotaTypes.NETWORK_BYTES_RECEIVED:
case QuotaTypes.NETWORK_BYTES_SENT:
quotaListForAccount.add(updateQuotaNetwork(usageRecord, usageRecord.getUsageType()));
qu = updateQuotaNetwork(usageRecord, usageRecord.getUsageType());
if (qu != null) {
quotaListForAccount.add(qu);
}
break;
case QuotaTypes.VM_DISK_IO_READ:
case QuotaTypes.VM_DISK_IO_WRITE:
@ -182,9 +197,17 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
if (quotaListForAccount == null || quotaListForAccount.isEmpty()) {
return;
}
quotaListForAccount.add(new QuotaUsageVO());
if (s_logger.isDebugEnabled()) {
s_logger.debug(quotaListForAccount.get(0));
}
Date startDate = quotaListForAccount.get(0).getStartDate();
Date endDate = quotaListForAccount.get(0).getEndDate();
if (s_logger.isDebugEnabled()) {
s_logger.debug("processQuotaBalanceForAccount startDate " + startDate + " endDate=" + endDate);
s_logger.debug("processQuotaBalanceForAccount last items startDate " + quotaListForAccount.get(quotaListForAccount.size() - 1).getStartDate() + " items endDate="
+ quotaListForAccount.get(quotaListForAccount.size() - 1).getEndDate());
}
quotaListForAccount.add(new QuotaUsageVO());
BigDecimal aggrUsage = new BigDecimal(0);
List<QuotaBalanceVO> creditsReceived = null;
@ -192,14 +215,21 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
QuotaUsageVO lastQuotaUsage = _quotaUsageDao.findLastQuotaUsageEntry(account.getAccountId(), account.getDomainId(), startDate);
if (lastQuotaUsage == null) {
creditsReceived = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), new Date(0), startDate);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Credit entries count " + creditsReceived.size() + " on Before Date=" + startDate);
}
if (creditsReceived != null) {
for (QuotaBalanceVO credit : creditsReceived) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Credit entry found " + aggrUsage.setScale(6, RoundingMode.HALF_EVEN).toString() + " on Date=" + startDate);
s_logger.debug("Credit entry found " + credit);
s_logger.debug("Total = " + aggrUsage);
}
aggrUsage = aggrUsage.add(credit.getCreditBalance());
}
}
// create a balance entry for these accumulated credits
QuotaBalanceVO firstBalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, startDate);
_quotaBalanceDao.saveQuotaBalance(firstBalance);
}
for (QuotaUsageVO entry : quotaListForAccount) {
if (entry.getQuotaUsed().compareTo(BigDecimal.ZERO) == 0) {
@ -208,7 +238,8 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
if (creditsReceived != null) {
for (QuotaBalanceVO credit : creditsReceived) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Credit entry found " + aggrUsage.setScale(6, RoundingMode.HALF_EVEN).toString() + " on Date=" + entry.getStartDate());
s_logger.debug("Credit entry found " + credit);
s_logger.debug("Total = " + aggrUsage);
}
aggrUsage = aggrUsage.add(credit.getCreditBalance());
}
@ -216,16 +247,14 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
continue;
}
if (s_logger.isDebugEnabled()) {
s_logger.debug(account.getAccountName() + ", Entry=" + entry.getQuotaUsed().setScale(6, RoundingMode.HALF_EVEN).toString() + " SD=" + entry.getStartDate() + " ED="
+ entry.getEndDate());
s_logger.debug("Entry=" + entry);
}
if (startDate.compareTo(entry.getStartDate()) != 0) {
if (s_logger.isDebugEnabled()) {
s_logger.debug(
"Saving Balance" + account.getAccountName() + ",Balance entry=" + aggrUsage.setScale(6, RoundingMode.HALF_EVEN).toString() + " on Date=" + endDate);
}
QuotaBalanceVO newBalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
_quotaBalanceDao.saveQuotaBalance(newBalance);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Saving Balance" + newBalance);
}
//New balance entry
aggrUsage = new BigDecimal(0);
@ -245,26 +274,34 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
}
}
if (s_logger.isDebugEnabled()) {
s_logger.debug(
"Getting Balance" + account.getAccountName() + ",Balance entry=" + aggrUsage.setScale(6, RoundingMode.HALF_EVEN).toString() + " on Date=" + endDate);
s_logger.debug("Getting Balance" + account.getAccountName() + ",Balance entry=" + aggrUsage + " on Date=" + endDate);
}
}
aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
}
QuotaBalanceVO newBalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
_quotaBalanceDao.saveQuotaBalance(newBalance);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Saving Balance" + newBalance);
}
// update quota_accounts
QuotaAccountVO quota_account = _quotaAcc.findByIdQuotaAccount(account.getAccountId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Updating quota account bal=" + aggrUsage.setScale(6, RoundingMode.HALF_EVEN).toString() + " date=" + endDate);
}
if (quota_account == null) {
quota_account = new QuotaAccountVO(account.getAccountId());
quota_account.setQuotaBalance(aggrUsage);
quota_account.setQuotaBalanceDate(endDate);
if (s_logger.isDebugEnabled()) {
s_logger.debug(quota_account);
}
_quotaAcc.persistQuotaAccount(quota_account);
} else {
quota_account.setQuotaBalance(aggrUsage);
quota_account.setQuotaBalanceDate(endDate);
if (s_logger.isDebugEnabled()) {
s_logger.debug(quota_account);
}
_quotaAcc.updateQuotaAccount(account.getAccountId(), quota_account);
}
}
@ -274,6 +311,9 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
List<AccountVO> accounts = _accountDao.listAll();
for (AccountVO account : accounts) {
Pair<List<? extends UsageVO>, Integer> usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Usage entries size = " + usageRecords.second().intValue() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
}
List<QuotaUsageVO> quotaListForAccount = aggregatePendingQuotaRecordsForAccount(account, usageRecords);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Quota entries size = " + quotaListForAccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
@ -286,7 +326,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
public QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
QuotaUsageVO quota_usage = null;
QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal quotaUsgage;
BigDecimal onehourcostpergb;
BigDecimal noofgbinuse;
@ -313,7 +353,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
rawusage = new BigDecimal(usageRecord.getRawUsage());
QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
onehourcostpercpu = tariff.getCurrencyValue().multiply(aggregationRatio);
cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
@ -323,7 +363,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
quotalist.add(quota_usage);
}
tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal speed = new BigDecimal(serviceoffering.getSpeed() / 100.00);
onehourcostper100mhz = tariff.getCurrencyValue().multiply(aggregationRatio);
speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
@ -333,7 +373,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
quotalist.add(quota_usage);
}
tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
onehourcostper1mb = tariff.getCurrencyValue().multiply(aggregationRatio);
memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
@ -343,7 +383,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
quotalist.add(quota_usage);
}
tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
vmusage = rawusage.multiply(onehourcostforvmusage);
quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
@ -360,7 +400,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
public QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
QuotaUsageVO quota_usage = null;
QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal vmusage;
BigDecimal onehourcostforvmusage;
onehourcostforvmusage = tariff.getCurrencyValue().multiply(aggregationRatio);
@ -378,7 +418,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
public QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
QuotaUsageVO quota_usage = null;
QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal ruleusage;
BigDecimal onehourcost;
onehourcost = tariff.getCurrencyValue().multiply(aggregationRatio);
@ -396,7 +436,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
public QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
QuotaUsageVO quota_usage = null;
QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
if (tariff != null && !tariff.getCurrencyValue().equals(0)) {
if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
BigDecimal onegbcost;
BigDecimal rawusageingb;
BigDecimal networkusage;

View File

@ -156,7 +156,7 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
// get records before startDate to find start balance
for (QuotaBalanceVO entry : quotaUsageRecords) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("FindQuotaBalIance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
s_logger.debug("FindQuotaBalIance Entry=" + entry);
}
if (entry.getCreditsId() > 0) {
trimmedRecords.add(entry);
@ -179,7 +179,7 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
}
for (QuotaBalanceVO entry : quotaBalance) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("lastQuotaBalance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
s_logger.debug("lastQuotaBalance Entry=" + entry);
}
finalBalance = finalBalance.add(entry.getCreditBalance());
}

View File

@ -139,4 +139,11 @@ public class QuotaAccountVO implements InternalIdentity {
this.lastStatementDate = lastStatementDate;
}
@Override
public String toString() {
return "QuotaAccountVO [accountId=" + accountId + ", quotaEnforce=" + quotaEnforce + ", quotaBalance=" + quotaBalance + ", quotaBalanceDate=" + quotaBalanceDate
+ ", quotaMinBalance=" + quotaMinBalance + ", quotaAlertType=" + quotaAlertType + ", quotaAlertDate=" + quotaAlertDate + ", lastStatementDate=" + lastStatementDate
+ "]";
}
}

View File

@ -71,7 +71,7 @@ public class QuotaBalanceVO implements InternalIdentity {
this.accountId = accountId;
this.domainId = domainId;
this.creditBalance = creditBalance;
this.creditsId=0L;
this.creditsId = 0L;
this.updatedOn = updatedOn;
}
@ -123,4 +123,11 @@ public class QuotaBalanceVO implements InternalIdentity {
public void setUpdatedOn(Date updatedOn) {
this.updatedOn = updatedOn;
}
@Override
public String toString() {
return "QuotaBalanceVO [id=" + id + ", accountId=" + accountId + ", domainId=" + domainId + ", creditBalance=" + creditBalance + ", creditsId=" + creditsId + ", updatedOn="
+ updatedOn + "]";
}
}

View File

@ -65,13 +65,13 @@ public class QuotaUsageVO implements InternalIdentity {
private Date endDate = null;
public QuotaUsageVO() {
usageType=-1;
usageType = -1;
quotaUsed = new BigDecimal(0);
endDate = new Date();
endDate = new Date();
startDate = new Date();
}
public QuotaUsageVO(Long usageItemId, Long zoneId, Long accountId, Long domainId, int usageType, BigDecimal quotaUsed, Date startDate, Date endDate) {
public QuotaUsageVO(Long usageItemId, Long zoneId, Long accountId, Long domainId, int usageType, BigDecimal quotaUsed, Date startDate, Date endDate) {
super();
this.usageItemId = usageItemId;
this.zoneId = zoneId;
@ -168,4 +168,10 @@ public class QuotaUsageVO implements InternalIdentity {
this.id = id;
}
@Override
public String toString() {
return "QuotaUsageVO [id=" + id + ", zoneId=" + zoneId + ", accountId=" + accountId + ", domainId=" + domainId + ", usageItemId=" + usageItemId + ", usageType=" + usageType
+ ", quotaUsed=" + quotaUsed + ", startDate=" + startDate + ", endDate=" + endDate + "]";
}
}

View File

@ -66,6 +66,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@Component
@Local(value = QuotaResponseBuilderImpl.class)
@ -181,6 +182,33 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
}
});
boolean have_balance_entries = false;
//check that there is at least one balance entry
for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
QuotaBalanceVO entry = it.next();
if (entry.getCreditsId() > 0) {
have_balance_entries = true;
break;
}
}
//if last entry is a credit deposit then remove that as that is already
//accounted for in the starting balance after that entry, note the sort is desc
if (have_balance_entries) {
ListIterator<QuotaBalanceVO> li = quotaBalance.listIterator(quotaBalance.size());
// Iterate in reverse.
while (li.hasPrevious()) {
QuotaBalanceVO entry = li.previous();
if (s_logger.isDebugEnabled()) {
s_logger.debug("createQuotaBalanceResponse: Entry=" + entry);
}
if (entry.getCreditsId() > 0) {
li.remove();
} else {
break;
}
}
}
int quota_activity = quotaBalance.size();
QuotaBalanceResponse resp = new QuotaBalanceResponse();
BigDecimal lastCredits = new BigDecimal(0);
@ -188,8 +216,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
QuotaBalanceVO entry = it.next();
if (s_logger.isDebugEnabled()) {
s_logger.debug(
"createQuotaBalanceResponse: Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
s_logger.debug("createQuotaBalanceResponse: All Credit Entry=" + entry);
}
if (entry.getCreditsId() > 0) {
if (consecutive) {
@ -206,9 +233,13 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
// order is desc last item is the start item
QuotaBalanceVO startItem = quotaBalance.get(quotaBalance.size() - 1);
QuotaBalanceVO endItem = quotaBalance.get(0);
resp.setStartDate(startDate);
resp.setStartDate(startItem.getUpdatedOn());
resp.setStartQuota(startItem.getCreditBalance());
resp.setEndDate(endDate);
resp.setEndDate(endItem.getUpdatedOn());
if (s_logger.isDebugEnabled()) {
s_logger.debug("createQuotaBalanceResponse: Start Entry=" + startItem);
s_logger.debug("createQuotaBalanceResponse: End Entry=" + endItem);
}
resp.setEndQuota(endItem.getCreditBalance().add(lastCredits));
} else if (quota_activity > 0) {
// order is desc last item is the start item