Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public class ApiConstants {
public static final String BACKUP_VM_OFFERING_REMOVED = "vmbackupofferingremoved";
public static final String IS_BACKUP_VM_EXPUNGED = "isbackupvmexpunged";
public static final String BACKUP_TOTAL = "backuptotal";
public static final String BALANCE = "balance";
public static final String BALANCES = "balances";
public static final String BASE64_IMAGE = "base64image";
public static final String BGP_PEERS = "bgppeers";
public static final String BGP_PEER_IDS = "bgppeerids";
Expand Down Expand Up @@ -157,6 +159,7 @@ public class ApiConstants {
public static final String CUSTOM_ID = "customid";
public static final String CUSTOM_ACTION_ID = "customactionid";
public static final String CUSTOM_JOB_ID = "customjobid";
public static final String CURRENCY = "currency";
public static final String CURRENT_START_IP = "currentstartip";
public static final String CURRENT_END_IP = "currentendip";
public static final String ENCRYPT = "encrypt";
Expand All @@ -170,6 +173,7 @@ public class ApiConstants {
public static final String DATACENTER_NAME = "datacentername";
public static final String DATADISKS_DETAILS = "datadisksdetails";
public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist";
public static final String DATE = "date";
public static final String DEFAULT_VALUE = "defaultvalue";
public static final String DELETE_PROTECTION = "deleteprotection";
public static final String DESCRIPTION = "description";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,9 @@ protected void processQuotaBalanceForAccount(AccountVO accountVo, List<QuotaUsag
return;
}

Date startDate = accountQuotaUsages.get(0).getStartDate();
Date endDate = accountQuotaUsages.get(0).getEndDate();
QuotaUsageVO firstQuotaUsage = accountQuotaUsages.get(0);
Date startDate = firstQuotaUsage.getStartDate();
Date endDate = firstQuotaUsage.getEndDate();
Date lastQuotaUsageEndDate = accountQuotaUsages.get(accountQuotaUsages.size() - 1).getEndDate();

LinkedHashSet<Pair<Date, Date>> periods = accountQuotaUsages.stream()
Expand Down Expand Up @@ -215,7 +216,7 @@ protected BigDecimal retrieveBalanceForUsageCalculation(long accountId, long dom
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, startDate);
QuotaBalanceVO lastRealBalance = _quotaBalanceDao.getLastQuotaBalanceEntry(accountId, domainId, startDate);

if (lastRealBalance == null) {
logger.warn("Account [{}] has quota usage entries, however it does not have a quota balance.", accountToString);
Expand Down Expand Up @@ -244,7 +245,7 @@ protected void saveQuotaAccount(long accountId, BigDecimal aggregatedUsage, Date
}

protected BigDecimal aggregateCreditBetweenDates(Long accountId, Long domainId, Date startDate, Date endDate, String accountToString) {
List<QuotaBalanceVO> creditsReceived = _quotaBalanceDao.findCreditBalance(accountId, domainId, startDate, endDate);
List<QuotaBalanceVO> creditsReceived = _quotaBalanceDao.findCreditBalances(accountId, domainId, startDate, endDate);
logger.debug("Account [{}] has [{}] credit entries before [{}].", accountToString, creditsReceived.size(),
DateUtil.displayDateInTimezone(usageAggregationTimeZone, endDate));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,13 @@ public interface QuotaBalanceDao extends GenericDao<QuotaBalanceVO, Long> {

QuotaBalanceVO saveQuotaBalance(QuotaBalanceVO qb);

List<QuotaBalanceVO> findCreditBalance(Long accountId, Long domainId, Date startDate, Date endDate);
List<QuotaBalanceVO> findCreditBalances(Long accountId, Long domainId, Date startDate, Date endDate);

QuotaBalanceVO findLastBalanceEntry(Long accountId, Long domainId, Date beforeThis);
QuotaBalanceVO getLastQuotaBalanceEntry(Long accountId, Long domainId, Date beforeThis);

QuotaBalanceVO findLaterBalanceEntry(Long accountId, Long domainId, Date afterThis);

List<QuotaBalanceVO> findQuotaBalance(Long accountId, Long domainId, Date startDate, Date endDate);

List<QuotaBalanceVO> lastQuotaBalanceVO(Long accountId, Long domainId, Date startDate);

BigDecimal lastQuotaBalance(Long accountId, Long domainId, Date startDate);
List<QuotaBalanceVO> listQuotaBalances(Long accountId, Long domainId, Date startDate, Date endDate);
BigDecimal getLastQuotaBalance(Long accountId, Long domainId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import com.cloud.utils.db.Filter;
Expand All @@ -32,160 +35,104 @@
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.db.TransactionStatus;

@Component
public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> implements QuotaBalanceDao {
private static final Logger logger = LogManager.getLogger(QuotaBalanceDaoImpl.class);

@Override
public QuotaBalanceVO findLastBalanceEntry(final Long accountId, final Long domainId, final Date beforeThis) {
return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<QuotaBalanceVO>() {
@Override
public QuotaBalanceVO doInTransaction(final TransactionStatus status) {
List<QuotaBalanceVO> quotaBalanceEntries = new ArrayList<>();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
QueryBuilder<QuotaBalanceVO> qb = QueryBuilder.create(QuotaBalanceVO.class);
qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.EQ, 0);
public QuotaBalanceVO getLastQuotaBalanceEntry(final Long accountId, final Long domainId, final Date beforeThis) {
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<QuotaBalanceVO>) status -> {
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
QueryBuilder<QuotaBalanceVO> qb = getQuotaBalanceQueryBuilder(accountId, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.EQ, 0);

if (beforeThis != null) {
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.LT, beforeThis);
quotaBalanceEntries = search(qb.create(), filter);
return !quotaBalanceEntries.isEmpty() ? quotaBalanceEntries.get(0) : null;
}

List<QuotaBalanceVO> quotaBalanceEntries = search(qb.create(), filter);
return !quotaBalanceEntries.isEmpty() ? quotaBalanceEntries.get(0) : null;
});
}

@Override
public QuotaBalanceVO findLaterBalanceEntry(final Long accountId, final Long domainId, final Date afterThis) {
return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<QuotaBalanceVO>() {
@Override
public QuotaBalanceVO doInTransaction(final TransactionStatus status) {
List<QuotaBalanceVO> quotaBalanceEntries = new ArrayList<>();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
QueryBuilder<QuotaBalanceVO> qb = QueryBuilder.create(QuotaBalanceVO.class);
qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.EQ, 0);
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.GT, afterThis);
quotaBalanceEntries = search(qb.create(), filter);
return quotaBalanceEntries.size() > 0 ? quotaBalanceEntries.get(0) : null;
}
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<QuotaBalanceVO>) status -> {
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
QueryBuilder<QuotaBalanceVO> qb = getQuotaBalanceQueryBuilder(accountId, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.EQ, 0);
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.GT, afterThis);

List<QuotaBalanceVO> quotaBalanceEntries = search(qb.create(), filter);
return !quotaBalanceEntries.isEmpty() ? quotaBalanceEntries.get(0) : null;
});
}

@Override
public QuotaBalanceVO saveQuotaBalance(final QuotaBalanceVO qb) {
return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<QuotaBalanceVO>() {
@Override
public QuotaBalanceVO doInTransaction(final TransactionStatus status) {
return persist(qb);
}
});
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<QuotaBalanceVO>) status -> persist(qb));
}

@Override
public List<QuotaBalanceVO> findCreditBalance(final Long accountId, final Long domainId, final Date lastbalancedate, final Date beforeThis) {
return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<List<QuotaBalanceVO>>() {
@Override
public List<QuotaBalanceVO> doInTransaction(final TransactionStatus status) {
if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
QueryBuilder<QuotaBalanceVO> qb = QueryBuilder.create(QuotaBalanceVO.class);
qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.GT, 0);
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
return search(qb.create(), filter);
} else {
return new ArrayList<QuotaBalanceVO>();
}
public List<QuotaBalanceVO> findCreditBalances(final Long accountId, final Long domainId, final Date startDate, final Date endDate) {
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<List<QuotaBalanceVO>>) status -> {
if (ObjectUtils.anyNull(startDate, endDate) || startDate.after(endDate)) {
return new ArrayList<>();
}
});
}

@Override
public List<QuotaBalanceVO> findQuotaBalance(final Long accountId, final Long domainId, final Date startDate, final Date endDate) {
return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<List<QuotaBalanceVO>>() {
@Override
public List<QuotaBalanceVO> doInTransaction(final TransactionStatus status) {
List<QuotaBalanceVO> quotaUsageRecords = null;
QueryBuilder<QuotaBalanceVO> qb = QueryBuilder.create(QuotaBalanceVO.class);
if (accountId != null) {
qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
}
if (domainId != null) {
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
}
if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.BETWEEN, startDate, endDate);
} else {
return Collections.<QuotaBalanceVO> emptyList();
}
quotaUsageRecords = listBy(qb.create());
if (quotaUsageRecords.size() == 0) {
quotaUsageRecords.addAll(lastQuotaBalanceVO(accountId, domainId, startDate));
}
return quotaUsageRecords;
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
QueryBuilder<QuotaBalanceVO> qb = getQuotaBalanceQueryBuilder(accountId, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.GT, 0);
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.BETWEEN, startDate, endDate);

}
return search(qb.create(), filter);
});

}

@Override
public List<QuotaBalanceVO> lastQuotaBalanceVO(final Long accountId, final Long domainId, final Date pivotDate) {
return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<List<QuotaBalanceVO>>() {
@Override
public List<QuotaBalanceVO> doInTransaction(final TransactionStatus status) {
List<QuotaBalanceVO> quotaUsageRecords = null;
List<QuotaBalanceVO> trimmedRecords = new ArrayList<QuotaBalanceVO>();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 100L);
// ASSUMPTION there will be less than 100 continuous credit
// transactions
QueryBuilder<QuotaBalanceVO> qb = QueryBuilder.create(QuotaBalanceVO.class);
if (accountId != null) {
qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
}
if (domainId != null) {
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
}
if ((pivotDate != null)) {
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.LTEQ, pivotDate);
}
quotaUsageRecords = search(qb.create(), filter);

// get records before startDate to find start balance
for (QuotaBalanceVO entry : quotaUsageRecords) {
if (logger.isDebugEnabled()) {
logger.debug("FindQuotaBalance Entry=" + entry);
}
if (entry.getCreditsId() > 0) {
trimmedRecords.add(entry);
} else {
trimmedRecords.add(entry);
break; // add only consecutive credit entries and last balance entry
}
}
return trimmedRecords;
public List<QuotaBalanceVO> listQuotaBalances(final Long accountId, final Long domainId, final Date startDate, final Date endDate) {
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<List<QuotaBalanceVO>>) status -> {
QueryBuilder<QuotaBalanceVO> qb = getQuotaBalanceQueryBuilder(accountId, domainId);
qb.and(qb.entity().getCreditsId(), SearchCriteria.Op.EQ, 0);
if (startDate != null) {
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.GTEQ, startDate);
}
if (endDate != null) {
qb.and(qb.entity().getUpdatedOn(), SearchCriteria.Op.LTEQ, endDate);
}

Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true);
return listBy(qb.create(), filter);
});
}

@Override
public BigDecimal lastQuotaBalance(final Long accountId, final Long domainId, Date startDate) {
List<QuotaBalanceVO> quotaBalance = lastQuotaBalanceVO(accountId, domainId, startDate);
BigDecimal finalBalance = new BigDecimal(0);
if (quotaBalance.isEmpty()) {
logger.info("There are no balance entries on or before the requested date.");
return finalBalance;
public BigDecimal getLastQuotaBalance(final Long accountId, final Long domainId) {
QuotaBalanceVO quotaBalance = getLastQuotaBalanceEntry(accountId, domainId, null);
BigDecimal finalBalance = BigDecimal.ZERO;
Date startDate = DateUtils.addDays(new Date(), -1);
if (quotaBalance == null) {
logger.info("There are no balance entries for account [{}] and domain [{}]. Considering only new added credits.", accountId, domainId);
} else {
finalBalance = quotaBalance.getCreditBalance();
startDate = quotaBalance.getUpdatedOn();
}
for (QuotaBalanceVO entry : quotaBalance) {
if (logger.isDebugEnabled()) {
logger.debug("lastQuotaBalance Entry=" + entry);
}
finalBalance = finalBalance.add(entry.getCreditBalance());

List<QuotaBalanceVO> credits = findCreditBalances(accountId, domainId, startDate, new Date());

for (QuotaBalanceVO credit : credits) {
finalBalance = finalBalance.add(credit.getCreditBalance());
}

return finalBalance;
}

private QueryBuilder<QuotaBalanceVO> getQuotaBalanceQueryBuilder(Long accountId, Long domainId) {
QueryBuilder<QuotaBalanceVO> qb = QueryBuilder.create(QuotaBalanceVO.class);
qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
return qb;
}

}
Loading
Loading