CLOUDSTACK-8592: Add more unit tests

- unit tests for UsageTypes
- unit tests for all Cmd classes
- unit tests for all service and manager impls
- try-catch-finally or try-with-resource in dao impls for failsafe db switching
- remove dead code
- add missing quota calculation case (regression fixed)
- replace tabs with spaces in pom.xmls

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2015-09-01 11:51:00 +05:30 committed by Abhinandan Prateek
parent 9808321cf3
commit 892a6ebbe5
41 changed files with 2015 additions and 596 deletions

View File

@ -38,7 +38,6 @@ import com.cloud.utils.Pair;
@APICommand(name = "listUsageRecords", description = "Lists usage records for accounts", responseObject = UsageRecordResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class GetUsageRecordsCmd extends BaseListCmd {
public static final Logger s_logger = Logger.getLogger(GetUsageRecordsCmd.class.getName());
private static final String s_name = "listusagerecordsresponse";

View File

@ -22,7 +22,6 @@ import java.util.List;
import org.apache.cloudstack.api.response.UsageTypeResponse;
public class UsageTypes {
/* Any changes here should also reflect in cloud_usage.quota_mapping table */
public static final int RUNNING_VM = 1;
public static final int ALLOCATED_VM = 2; // used for tracking how long storage has been allocated for a VM

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.api.command.test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
@ -70,4 +71,22 @@ public class UsageCmdTest extends TestCase {
}
@Test
public void testCrud() {
getUsageRecordsCmd.setDomainId(1L);
assertTrue(getUsageRecordsCmd.getDomainId().equals(1L));
getUsageRecordsCmd.setAccountName("someAccount");
assertTrue(getUsageRecordsCmd.getAccountName().equals("someAccount"));
Date d = new Date();
getUsageRecordsCmd.setStartDate(d);
getUsageRecordsCmd.setEndDate(d);
assertTrue(getUsageRecordsCmd.getStartDate().equals(d));
assertTrue(getUsageRecordsCmd.getEndDate().equals(d));
getUsageRecordsCmd.setUsageId("someId");
assertTrue(getUsageRecordsCmd.getUsageId().equals("someId"));
}
}

View File

@ -339,4 +339,48 @@ public class UsageVO implements Usage, InternalIdentity {
public Date getEndDate() {
return endDate;
}
public void setId(Long id) {
this.id = id;
}
public void setType(String type) {
this.type = type;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
public void setUsageType(int usageType) {
this.usageType = usageType;
}
public void setRawUsage(Double rawUsage) {
this.rawUsage = rawUsage;
}
public void setSize(Long size) {
this.size = size;
}
public void setVirtualSize(Long virtualSize) {
this.virtualSize = virtualSize;
}
}

View File

@ -42,30 +42,35 @@ import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
@Component
@Local(value = { UsageDao.class })
@Local(value = {UsageDao.class})
public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements UsageDao {
public static final Logger s_logger = Logger.getLogger(UsageDaoImpl.class.getName());
private static final String DELETE_ALL = "DELETE FROM cloud_usage";
private static final String DELETE_ALL_BY_ACCOUNTID = "DELETE FROM cloud_usage WHERE account_id = ?";
private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, type, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?)";
private static final String INSERT_USER_STATS = "INSERT INTO cloud_usage.user_statistics (id, data_center_id, account_id, public_ip_address, device_id, device_type, network_id, net_bytes_received,"
+ " net_bytes_sent, current_bytes_received, current_bytes_sent, agg_bytes_received, agg_bytes_sent) VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?)";
private static final String INSERT_USER_STATS =
"INSERT INTO cloud_usage.user_statistics (id, data_center_id, account_id, public_ip_address, device_id, device_type, network_id, net_bytes_received,"
+ " net_bytes_sent, current_bytes_received, current_bytes_sent, agg_bytes_received, agg_bytes_sent) VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?)";
private static final String UPDATE_ACCOUNT = "UPDATE cloud_usage.account SET account_name=?, removed=? WHERE id=?";
private static final String UPDATE_USER_STATS = "UPDATE cloud_usage.user_statistics SET net_bytes_received=?, net_bytes_sent=?, current_bytes_received=?, current_bytes_sent=?, agg_bytes_received=?, agg_bytes_sent=? WHERE id=?";
private static final String UPDATE_USER_STATS =
"UPDATE cloud_usage.user_statistics SET net_bytes_received=?, net_bytes_sent=?, current_bytes_received=?, current_bytes_sent=?, agg_bytes_received=?, agg_bytes_sent=? WHERE id=?";
private static final String GET_LAST_ACCOUNT = "SELECT id FROM cloud_usage.account ORDER BY id DESC LIMIT 1";
private static final String GET_LAST_USER_STATS = "SELECT id FROM cloud_usage.user_statistics ORDER BY id DESC LIMIT 1";
private static final String GET_PUBLIC_TEMPLATES_BY_ACCOUNTID = "SELECT id FROM cloud.vm_template WHERE account_id = ? AND public = '1' AND removed IS NULL";
private static final String GET_LAST_VM_DISK_STATS = "SELECT id FROM cloud_usage.vm_disk_statistics ORDER BY id DESC LIMIT 1";
private static final String INSERT_VM_DISK_STATS = "INSERT INTO cloud_usage.vm_disk_statistics (id, data_center_id, account_id, vm_id, volume_id, net_io_read, net_io_write, current_io_read, "
+ "current_io_write, agg_io_read, agg_io_write, net_bytes_read, net_bytes_write, current_bytes_read, current_bytes_write, agg_bytes_read, agg_bytes_write) "
+ " VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?, ?,?, ?, ?)";
private static final String UPDATE_VM_DISK_STATS = "UPDATE cloud_usage.vm_disk_statistics SET net_io_read=?, net_io_write=?, current_io_read=?, current_io_write=?, agg_io_read=?, agg_io_write=?, "
+ "net_bytes_read=?, net_bytes_write=?, current_bytes_read=?, current_bytes_write=?, agg_bytes_read=?, agg_bytes_write=? WHERE id=?";
private static final String INSERT_VM_DISK_STATS =
"INSERT INTO cloud_usage.vm_disk_statistics (id, data_center_id, account_id, vm_id, volume_id, net_io_read, net_io_write, current_io_read, "
+ "current_io_write, agg_io_read, agg_io_write, net_bytes_read, net_bytes_write, current_bytes_read, current_bytes_write, agg_bytes_read, agg_bytes_write) "
+ " VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?, ?,?, ?, ?)";
private static final String UPDATE_VM_DISK_STATS =
"UPDATE cloud_usage.vm_disk_statistics SET net_io_read=?, net_io_write=?, current_io_read=?, current_io_write=?, agg_io_read=?, agg_io_write=?, "
+ "net_bytes_read=?, net_bytes_write=?, current_bytes_read=?, current_bytes_write=?, agg_bytes_read=?, agg_bytes_write=? WHERE id=?";
private static final String INSERT_USAGE_RECORDS = "INSERT INTO cloud_usage.cloud_usage (zone_id, account_id, domain_id, description, usage_display, "
+ "usage_type, raw_usage, vm_instance_id, vm_name, offering_id, template_id, "
+
"usage_type, raw_usage, vm_instance_id, vm_name, offering_id, template_id, "
+ "usage_id, type, size, network_id, start_date, end_date, virtual_size) VALUES (?,?,?,?,?,?,?,?,?, ?, ?, ?,?,?,?,?,?,?)";
protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT");
@ -106,9 +111,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = INSERT_ACCOUNT;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (AccountVO acct : accounts) {
pstmt.setLong(1, acct.getId());
pstmt.setString(2, acct.getAccountName());
@ -142,9 +145,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = UPDATE_ACCOUNT;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (AccountVO acct : accounts) {
pstmt.setString(1, acct.getAccountName());
@ -174,9 +175,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = INSERT_USER_STATS;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (UserStatisticsVO userStat : userStats) {
pstmt.setLong(1, userStat.getId());
pstmt.setLong(2, userStat.getDataCenterId());
@ -217,9 +216,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = UPDATE_USER_STATS;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (UserStatisticsVO userStat : userStats) {
pstmt.setLong(1, userStat.getNetBytesReceived());
pstmt.setLong(2, userStat.getNetBytesSent());
@ -316,9 +313,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = UPDATE_VM_DISK_STATS;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (VmDiskStatisticsVO vmDiskStat : vmDiskStats) {
pstmt.setLong(1, vmDiskStat.getNetIORead());
pstmt.setLong(2, vmDiskStat.getNetIOWrite());
@ -352,9 +347,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = INSERT_VM_DISK_STATS;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (VmDiskStatisticsVO vmDiskStat : vmDiskStats) {
pstmt.setLong(1, vmDiskStat.getId());
pstmt.setLong(2, vmDiskStat.getDataCenterId());
@ -400,9 +393,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
txn.start();
String sql = INSERT_USAGE_RECORDS;
PreparedStatement pstmt = null;
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just
// want CLOUD_USAGE
// dataSource connection
pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
for (UsageVO usageRecord : usageRecords) {
pstmt.setLong(1, usageRecord.getZoneId());
pstmt.setLong(2, usageRecord.getAccountId());
@ -464,26 +455,32 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
@Override
@SuppressWarnings("deprecation")
public Pair<List<? extends UsageVO>, Integer> getUsageRecordsPendingQuotaAggregation(final long accountId, final long domainId) {
if (s_logger.isDebugEnabled()){
s_logger.debug("Getting usage records for account: " + accountId + ", domainId: " + domainId);
}
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
if (s_logger.isDebugEnabled()){
s_logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId);
Pair<List<UsageVO>, Integer> usageRecords = new Pair<List<UsageVO>, Integer>(new ArrayList<UsageVO>(), 0);
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
SearchCriteria<UsageVO> sc = createSearchCriteria();
if (accountId != -1) {
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
}
if (domainId != -1) {
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
}
sc.addAnd("quotaCalculated", SearchCriteria.Op.NULL);
sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
if (s_logger.isDebugEnabled()){
s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
}
usageRecords = searchAndCountAllRecords(sc, usageFilter);
} catch (Exception e) {
s_logger.error("getUsageRecordsPendingQuotaAggregation failed due to: " + e.getMessage());
} finally {
TransactionLegacy.open(opendb).close();
}
Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L);
SearchCriteria<UsageVO> sc = createSearchCriteria();
if (accountId != -1) {
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
}
if (domainId != -1) {
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
}
sc.addAnd("quotaCalculated", SearchCriteria.Op.NULL);
sc.addOr("quotaCalculated", SearchCriteria.Op.EQ, 0);
if (s_logger.isDebugEnabled()){
s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
}
Pair<List<UsageVO>, Integer> usageRecords = searchAndCountAllRecords(sc, usageFilter);
TransactionLegacy.open(opendb).close();
return new Pair<List<? extends UsageVO>, Integer>(usageRecords.first(), usageRecords.second());
}
}

View File

@ -37,5 +37,28 @@
<artifactId>cloud-engine-schema</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${cs.junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${cs.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${cs.powermock.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${cs.powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -90,6 +90,10 @@ public class QuotaTypes extends UsageTypes {
}
static public String getDescription(int quotaType) {
return listQuotaTypes().get(quotaType).getDescription();
HashMap<Integer, QuotaTypes> quotaMap = listQuotaTypes();
if (quotaMap.containsKey(quotaType)) {
return quotaMap.get(quotaType).getDescription();
}
return null;
}
}

View File

@ -16,27 +16,34 @@
//under the License.
package org.apache.cloudstack.quota.dao;
import java.util.List;
import javax.ejb.Local;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.springframework.stereotype.Component;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.ejb.Local;
import java.util.List;
@Component
@Local(value = { QuotaAccountDao.class })
public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> implements QuotaAccountDao {
public static final Logger s_logger = Logger.getLogger(QuotaAccountDaoImpl.class.getName());
@Override
public List<QuotaAccountVO> listAll() {
List<QuotaAccountVO> result = null;
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.listAll();
TransactionLegacy.open(opendb).close();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.listAll();
} catch (Exception e) {
s_logger.error("QuotaAccountDaoImpl::listAll() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to list Quota Accounts");
} finally {
TransactionLegacy.open(opendb).close();
}
return result;
}
@ -44,9 +51,15 @@ public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> im
public QuotaAccountVO findById(Long id) {
QuotaAccountVO result = null;
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.findById(id);
TransactionLegacy.open(opendb).close();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.findById(id);
} catch (Exception e) {
s_logger.error("QuotaAccountDaoImpl::findById() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find Quota Account by ID");
} finally {
TransactionLegacy.open(opendb).close();
}
return result;
}
@ -54,9 +67,15 @@ public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> im
public QuotaAccountVO persist(QuotaAccountVO entity) {
QuotaAccountVO result = null;
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.persist(entity);
TransactionLegacy.open(opendb).close();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.persist(entity);
} catch (Exception e) {
s_logger.error("QuotaAccountDaoImpl::persist() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to save Quota Account");
} finally {
TransactionLegacy.open(opendb).close();
}
return result;
}
@ -64,9 +83,15 @@ public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> im
public boolean update(Long id, QuotaAccountVO entity) {
boolean result = false;
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.update(id, entity);
TransactionLegacy.open(opendb).close();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
result = super.update(id, entity);
} catch (Exception e) {
s_logger.error("QuotaAccountDaoImpl::update() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to update Quota Account");
} finally {
TransactionLegacy.open(opendb).close();
}
return result;
}

View File

@ -16,23 +16,21 @@
//under the License.
package org.apache.cloudstack.quota.dao;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Local;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.log4j.Logger;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.ejb.Local;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
@Local(value = { QuotaBalanceDao.class })
@ -42,78 +40,97 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
@SuppressWarnings("deprecation")
@Override
public QuotaBalanceVO findLastBalanceEntry(final Long accountId, final Long domainId, final Date beforeThis) {
List<QuotaBalanceVO> quotaBalanceEntries = new ArrayList<>();
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
List<QuotaBalanceVO> quotab = this.search(sc, filter);
TransactionLegacy.open(opendb).close();
return quotab.size() > 0 ? quotab.get(0) : null;
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 1L);
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
sc.addAnd("updatedOn", SearchCriteria.Op.LT, beforeThis);
quotaBalanceEntries = this.search(sc, filter);
} catch (Exception e) {
s_logger.error("QuotaBalanceDaoImpl::findLastBalanceEntry() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find last quota balance entry for account");
} finally {
TransactionLegacy.open(opendb).close();
}
return quotaBalanceEntries.size() > 0 ? quotaBalanceEntries.get(0) : null;
}
@SuppressWarnings("deprecation")
@Override
public QuotaBalanceVO findLaterBalanceEntry(final Long accountId, final Long domainId, final Date afterThis) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
List<QuotaBalanceVO> quotab = this.search(sc, filter);
TransactionLegacy.open(opendb).close();
return quotab.size() > 0 ? quotab.get(0) : null;
List<QuotaBalanceVO> quotaBalanceEntries = new ArrayList<>();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L);
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
sc.addAnd("creditsId", SearchCriteria.Op.EQ, 0);
sc.addAnd("updatedOn", SearchCriteria.Op.GT, afterThis);
quotaBalanceEntries = this.search(sc, filter);
} catch (Exception e) {
s_logger.error("QuotaBalanceDaoImpl::findLaterBalanceEntry() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find later quota balance entry");
} finally {
TransactionLegacy.open(opendb).close();
}
return quotaBalanceEntries.size() > 0 ? quotaBalanceEntries.get(0) : null;
}
@Override
public void saveQuotaBalance(final List<QuotaBalanceVO> credits) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
try {
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
for (QuotaBalanceVO credit : credits) {
persist(credit);
}
} catch (Exception e) {
throw new CloudRuntimeException("Unable to save quota balance");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close();
}
@SuppressWarnings("deprecation")
@Override
public List<QuotaBalanceVO> findCreditBalance(final Long accountId, final Long domainId, final Date lastbalancedate, final Date beforeThis) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
sc.addAnd("creditsId", SearchCriteria.Op.GT, 0);
if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
} else {
return new ArrayList<QuotaBalanceVO>();
List<QuotaBalanceVO> quotaBalances = new ArrayList<>();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB).close();
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
sc.addAnd("creditsId", SearchCriteria.Op.GT, 0);
if ((lastbalancedate != null) && (beforeThis != null) && lastbalancedate.before(beforeThis)) {
sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, lastbalancedate, beforeThis);
} else {
return new ArrayList<QuotaBalanceVO>();
}
quotaBalances = search(sc, filter);
} catch (Exception e) {
s_logger.error("QuotaBalanceDaoImpl::findCreditBalance() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find quota credit balance");
} finally {
TransactionLegacy.open(opendb).close();
}
List<QuotaBalanceVO> qb = search(sc, filter);
TransactionLegacy.open(opendb).close();
return qb;
return quotaBalances;
}
@SuppressWarnings("deprecation")
@Override
public List<QuotaBalanceVO> findQuotaBalance(final Long accountId, final Long domainId, final Date startDate, final Date endDate) {
// TODO account for series of credits around boundaries
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
List<QuotaBalanceVO> quotaUsageRecords = null;
try {
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
SearchCriteria<QuotaBalanceVO> sc = createSearchCriteria();
if (accountId != null) {
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
@ -130,11 +147,12 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
if (quotaUsageRecords.size() == 0) {
quotaUsageRecords.addAll(lastQuotaBalanceVO(accountId, domainId, startDate));
}
} catch (Exception e) {
throw new CloudRuntimeException("Unable to find quota balance");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close();
return quotaUsageRecords;
}
@ -143,11 +161,9 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
@Override
public List<QuotaBalanceVO> lastQuotaBalanceVO(final Long accountId, final Long domainId, final Date pivotDate) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
List<QuotaBalanceVO> quotaUsageRecords = null;
List<QuotaBalanceVO> trimmedRecords = new ArrayList<QuotaBalanceVO>();
try {
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", false, 0L, 100L);
// ASSUMPTION there will be less than 100 continuous credit
// transactions
@ -164,8 +180,7 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
quotaUsageRecords = search(sc, filter);
// get records before startDate to find start balance
for (Iterator<QuotaBalanceVO> it = quotaUsageRecords.iterator(); it.hasNext();) {
QuotaBalanceVO entry = it.next();
for (QuotaBalanceVO entry : quotaUsageRecords) {
s_logger.info("findQuotaBalance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
if (entry.getCreditsId() > 0) {
trimmedRecords.add(entry);
@ -174,12 +189,11 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
break; // add only consecutive credit entries and last balance entry
}
}
} catch (Exception e) {
throw new CloudRuntimeException("Unable to get last quota balance");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close();
return trimmedRecords;
}
@ -190,10 +204,9 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase<QuotaBalanceVO, Long> im
new InvalidParameterValueException("There are no balance entries on or before the requested date.");
}
BigDecimal finalBalance = new BigDecimal(0);
for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
QuotaBalanceVO entry = it.next();
s_logger.info("lastQuotaBalance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
finalBalance.add(entry.getCreditBalance());
for (QuotaBalanceVO entry : quotaBalance) {
s_logger.debug("lastQuotaBalance Date=" + entry.getUpdatedOn().toGMTString() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
finalBalance = finalBalance.add(entry.getCreditBalance());
}
return finalBalance;
}

View File

@ -23,6 +23,8 @@ import java.util.List;
import javax.ejb.Local;
import javax.inject.Inject;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
@ -35,6 +37,8 @@ import com.cloud.utils.db.TransactionLegacy;
@Component
@Local(value = { QuotaCreditsDao.class })
public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> implements QuotaCreditsDao {
private static final Logger s_logger = Logger.getLogger(QuotaCreditsDaoImpl.class.getName());
@Inject
QuotaBalanceDao _quotaBalanceDao;
@ -42,31 +46,43 @@ public class QuotaCreditsDaoImpl extends GenericDaoBase<QuotaCreditsVO, Long> im
@Override
public List<QuotaCreditsVO> findCredits(final long accountId, final long domainId, final Date startDate, final Date endDate) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
} else {
return new ArrayList<QuotaCreditsVO>();
List<QuotaCreditsVO> qc = new ArrayList<>();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE);
SearchCriteria<QuotaCreditsVO> sc = createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
if ((startDate != null) && (endDate != null) && startDate.before(endDate)) {
sc.addAnd("updatedOn", SearchCriteria.Op.BETWEEN, startDate, endDate);
} else {
return new ArrayList<QuotaCreditsVO>();
}
qc = search(sc, filter);
} catch (Exception e) {
s_logger.error("QuotaCreditsDaoImpl::findCredits() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find quota credits");
} finally {
TransactionLegacy.open(opendb).close();
}
List<QuotaCreditsVO> qc = search(sc, filter);
TransactionLegacy.open(opendb).close();
return qc;
}
@Override
public QuotaCreditsVO saveCredits(final QuotaCreditsVO credits) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
persist(credits);
// make an entry in the balance table
QuotaBalanceVO bal = new QuotaBalanceVO(credits);
_quotaBalanceDao.persist(bal);
TransactionLegacy.open(opendb).close();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
super.persist(credits);
// make an entry in the balance table
QuotaBalanceVO bal = new QuotaBalanceVO(credits);
_quotaBalanceDao.persist(bal);
} catch (Exception e) {
s_logger.error("QuotaCreditsDaoImpl::saveCredits() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to save quota credits");
} finally {
TransactionLegacy.open(opendb).close();
}
return credits;
}
}

View File

@ -20,15 +20,19 @@ import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.ejb.Local;
import java.util.ArrayList;
import java.util.List;
@Component
@Local(value = { QuotaEmailTemplatesDao.class })
public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplatesVO, Long> implements QuotaEmailTemplatesDao {
private static final Logger s_logger = Logger.getLogger(QuotaEmailTemplatesDaoImpl.class.getName());
protected SearchBuilder<QuotaEmailTemplatesVO> QuotaEmailTemplateSearch;
@ -43,22 +47,36 @@ public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase<QuotaEmailTemplat
@Override
public List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
if (templateName != null) {
sc.setParameters("template_name", templateName);
List<QuotaEmailTemplatesVO> result = new ArrayList<>();
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
SearchCriteria<QuotaEmailTemplatesVO> sc = QuotaEmailTemplateSearch.create();
if (templateName != null) {
sc.setParameters("template_name", templateName);
}
result = this.listBy(sc);
} catch (Exception e) {
s_logger.error("QuotaEmailTemplatesDaoImpl::listAllQuotaEmailTemplates() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to list quota email templates");
} finally {
TransactionLegacy.open(opendb).close();
}
List<QuotaEmailTemplatesVO> result = this.listBy(sc);
TransactionLegacy.open(opendb).close();
return result;
}
@Override
public boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
final boolean result = this.update(template.getId(), template);
TransactionLegacy.open(opendb).close();
boolean result = false;
try {
TransactionLegacy.open(TransactionLegacy.USAGE_DB);
result = this.update(template.getId(), template);
} catch (Exception e) {
s_logger.error("QuotaEmailTemplatesDaoImpl::updateQuotaEmailTemplate() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to update quota email template");
} finally {
TransactionLegacy.open(opendb).close();
}
return result;
}
}

View File

@ -22,6 +22,7 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.log4j.Logger;
@ -56,27 +57,26 @@ public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> impl
@Override
public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
List<QuotaTariffVO> result = null;
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
try {
List<QuotaTariffVO> result = new ArrayList<>();
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
sc.setParameters("onorbefore", effectiveDate);
sc.setParameters("quotatype", quotaType);
result = search(sc, filter);
} catch (Exception e) {
throw new CloudRuntimeException("Unable to find tariff plan by usage type");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
// Switch back
TransactionLegacy.open(opendb).close();
if (result.size() > 0) {
if (s_logger.isDebugEnabled()){
s_logger.debug("findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType + " val=" + result.get(0).getCurrencyValue());
s_logger.debug("QuotaTariffDaoImpl::findTariffPlanByUsageType: " + effectiveDate + "quota type " + quotaType + " val=" + result.get(0).getCurrencyValue());
}
return result.get(0);
} else {
if (s_logger.isDebugEnabled()){
s_logger.info("Missing quota type " + quotaType);
s_logger.info("QuotaTariffDaoImpl::findTariffPlanByUsageType: Missing quota type " + quotaType);
}
return null;
}
@ -86,8 +86,7 @@ public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> impl
public List<QuotaTariffVO> listAllTariffPlans(final Date effectiveDate) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
List<QuotaTariffVO> tariffs = new ArrayList<QuotaTariffVO>();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
try {
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L);
final SearchCriteria<QuotaTariffVO> sc = listAllIncludedUsageType.create();
sc.setParameters("onorbefore", effectiveDate);
@ -96,47 +95,43 @@ public class QuotaTariffDaoImpl extends GenericDaoBase<QuotaTariffVO, Long> impl
List<QuotaTariffVO> result = search(sc, filter);
if (result.size() > 0) {
tariffs.add(result.get(0));
s_logger.info("listAllTariffPlans onorbefore" + effectiveDate + "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
s_logger.info("listAllTariffPlans onorbefore" + effectiveDate + "quota type " + result.get(0).getDescription() + " , effective Date=" + result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
}
}
} catch (Exception e) {
throw new CloudRuntimeException("Unable to list all tariff plans");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
// Switch back
TransactionLegacy.open(opendb).close();
return tariffs;
}
@Override
public boolean updateQuotaTariff(QuotaTariffVO plan) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
// to
boolean result = false;
try {
// Usage DB
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
result = this.update(plan.getId(), plan);
} catch (Exception e) {
throw new CloudRuntimeException("Unable to update quota tariff");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close(); // Switch back
return result;
}
@Override
public QuotaTariffVO addQuotaTariff(QuotaTariffVO plan) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); // Switch
// to
QuotaTariffVO result = null;
try {
// Usage DB
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
plan.setId(null);
result = this.persist(plan);
} catch (Exception e) {
throw new CloudRuntimeException("Unable to save quota tariff");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close(); // Switch back
return result;
}
}

View File

@ -16,25 +16,14 @@
//under the License.
package org.apache.cloudstack.quota.dao;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.SearchCriteria;
public interface QuotaUsageDao extends GenericDao<QuotaUsageVO, Long> {
List<QuotaUsageVO> findQuotaUsage(Long accountId, Long domainId, Integer usageType, Date startDate, Date endDate);
Pair<List<QuotaUsageVO>, Integer> searchAndCountAllRecords(SearchCriteria<QuotaUsageVO> sc, Filter filter);
void saveQuotaUsage(List<QuotaUsageVO> credits);
BigDecimal findTotalQuotaUsage(Long accountId, Long domainId, Integer usageType, Date startDate, Date endDate);
}

View File

@ -16,43 +16,30 @@
//under the License.
package org.apache.cloudstack.quota.dao;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.ejb.Local;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.Local;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
@Component
@Local(value = { QuotaUsageDao.class })
public class QuotaUsageDaoImpl extends GenericDaoBase<QuotaUsageVO, Long> implements QuotaUsageDao {
@Override
public Pair<List<QuotaUsageVO>, Integer> searchAndCountAllRecords(SearchCriteria<QuotaUsageVO> sc, Filter filter) {
return listAndCountIncludingRemovedBy(sc, filter);
}
@Override
public void saveQuotaUsage(List<QuotaUsageVO> records) {
for (QuotaUsageVO usageRecord : records) {
persist(usageRecord);
}
}
private static final Logger s_logger = Logger.getLogger(QuotaUsageDaoImpl.class.getName());
@Override
public BigDecimal findTotalQuotaUsage(final Long accountId, final Long domainId, final Integer usageType, final Date startDate, final Date endDate) {
List<QuotaUsageVO> quotaUsage = findQuotaUsage(accountId, domainId, null, startDate, endDate);
BigDecimal total = new BigDecimal(0);
for (final QuotaUsageVO quotaRecord : quotaUsage) {
for (QuotaUsageVO quotaRecord: quotaUsage) {
total = total.add(quotaRecord.getQuotaUsed());
}
return total;
@ -62,11 +49,9 @@ public class QuotaUsageDaoImpl extends GenericDaoBase<QuotaUsageVO, Long> implem
@Override
public List<QuotaUsageVO> findQuotaUsage(final Long accountId, final Long domainId, final Integer usageType, final Date startDate, final Date endDate) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
List<QuotaUsageVO> quotaUsageRecords = null;
try {
// TODO instead of max value query with reasonable number and
// iterate
List<QuotaUsageVO> quotaUsageRecords = new ArrayList<QuotaUsageVO>();
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
// TODO instead of max value query with reasonable number and iterate
SearchCriteria<QuotaUsageVO> sc = createSearchCriteria();
if (accountId != null) {
sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
@ -84,11 +69,12 @@ public class QuotaUsageDaoImpl extends GenericDaoBase<QuotaUsageVO, Long> implem
return new ArrayList<QuotaUsageVO>();
}
quotaUsageRecords = listBy(sc);
} catch (Exception e) {
s_logger.error("QuotaUsageDaoImpl::findQuotaUsage() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find quota usage");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close();
return quotaUsageRecords;
}

View File

@ -21,7 +21,5 @@ import org.apache.cloudstack.quota.vo.ServiceOfferingVO;
import com.cloud.utils.db.GenericDao;
public interface ServiceOfferingDao extends GenericDao<ServiceOfferingVO, Long> {
ServiceOfferingVO findServiceOffering(Long vmId, long serviceOfferingId);
}

View File

@ -43,18 +43,19 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Lo
@Override
public ServiceOfferingVO findServiceOffering(final Long vmId, final long serviceOfferingId) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
ServiceOfferingVO result;
try {
ServiceOfferingVO result = null;
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB)) {
result = findById(vmId, serviceOfferingId);
} catch (Exception e) {
s_logger.error("Quota ServiceOfferingDaoImpl::findServiceOffering() failed due to: " + e.getMessage());
throw new CloudRuntimeException("Unable to find service offering for quota calculations");
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close();
return result;
}
public ServiceOfferingVO findById(Long vmId, long serviceOfferingId) {
private ServiceOfferingVO findById(Long vmId, long serviceOfferingId) {
ServiceOfferingVO offering = super.findById(serviceOfferingId);
if (offering.isDynamic()) {
if (vmId == null) {
@ -67,7 +68,7 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Lo
return offering;
}
public ServiceOfferingVO getcomputeOffering(ServiceOfferingVO serviceOffering, Map<String, String> customParameters) {
private ServiceOfferingVO getcomputeOffering(ServiceOfferingVO serviceOffering, Map<String, String> customParameters) {
ServiceOfferingVO dummyoffering = new ServiceOfferingVO(serviceOffering);
dummyoffering.setDynamicFlag(true);
if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) {

View File

@ -24,8 +24,5 @@ import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.quota.vo.UserVmDetailVO;
public interface UserVmDetailsDao extends GenericDao<UserVmDetailVO, Long> {
Map<String, String> listDetailsKeyPairs(long resourceId);
}

View File

@ -48,11 +48,11 @@ public class UserVmDetailsDaoImpl extends GenericDaoBase<UserVmDetailVO, Long> i
@Override
public Map<String, String> listDetailsKeyPairs(long resourceId) {
Map<String, String> details = new HashMap<String, String>();
SearchCriteria<UserVmDetailVO> sc = AllFieldsSearch.create();
sc.setParameters("resourceId", resourceId);
List<UserVmDetailVO> results = search(sc, null);
Map<String, String> details = new HashMap<String, String>(results.size());
for (UserVmDetailVO result : results) {
details.put(result.getName(), result.getValue());
}

View File

@ -0,0 +1,47 @@
// 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.constant;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.UsageTypeResponse;
import org.apache.cloudstack.usage.UsageTypes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.HashMap;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaTypesTest extends TestCase {
@Test
public void testQuotaTypesList() {
HashMap<Integer, QuotaTypes> quotaTypes = QuotaTypes.listQuotaTypes();
List<UsageTypeResponse> usageTypesResponseList = UsageTypes.listUsageTypes();
for (UsageTypeResponse usageTypeResponse: usageTypesResponseList) {
final Integer usageTypeInt = usageTypeResponse.getUsageType();
assertTrue(quotaTypes.containsKey(usageTypeInt));
}
}
@Test
public void testQuotaTypeDescription() {
assertNull(QuotaTypes.getDescription(-1));
assertNotNull(QuotaTypes.getDescription(QuotaTypes.MEMORY));
}
}

View File

@ -1,102 +1,99 @@
<!-- 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. -->
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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-database-quota</artifactId>
<name>Apache CloudStack Plugin - Quota Service</name>
<parent>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloudstack-plugins</artifactId>
<version>4.6.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-engine-schema</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-framework-quota</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${cs.commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${cs.joda-time.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${cs.junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${cs.hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${cs.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${cs.powermock.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${cs.powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
</dependencies>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-database-quota</artifactId>
<name>Apache CloudStack Plugin - Quota Service</name>
<parent>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloudstack-plugins</artifactId>
<version>4.6.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-engine-schema</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-framework-quota</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${cs.commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${cs.joda-time.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${cs.junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${cs.hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${cs.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${cs.powermock.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${cs.powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
</dependencies>
</project>

View File

@ -112,11 +112,15 @@ public class QuotaCreditsCmd extends BaseCmd {
@Override
public void execute() {
Long accountId = _accountService.getActiveAccountByName(accountName, domainId).getAccountId();
Long accountId = null;
Account account = _accountService.getActiveAccountByName(accountName, domainId);
if (account != null) {
accountId = account.getAccountId();
}
if (accountId == null) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "The account does not exists or has been removed/disabled");
}
if (value == null) {
if (getValue() == null) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please send a valid non-empty quota value");
}
if (getQuotaEnforce() != null) {
@ -126,10 +130,10 @@ public class QuotaCreditsCmd extends BaseCmd {
_quotaService.setMinBalance(accountId, getMinBalance());
}
else {
_quotaService.setMinBalance(accountId, 0.2 * value);
_quotaService.setMinBalance(accountId, 0.2 * getValue());
}
final QuotaCreditsResponse response = _responseBuilder.addQuotaCredits(accountId, domainId, value, CallContext.current().getCallingUserId());
final QuotaCreditsResponse response = _responseBuilder.addQuotaCredits(accountId, getDomainId(), getValue(), CallContext.current().getCallingUserId());
response.setResponseName(getCommandName());
response.setObjectName("quotacredits");
setResponseObject(response);

View File

@ -41,6 +41,10 @@ public class QuotaEmailTemplateListCmd extends BaseListCmd {
return templateName;
}
public void setTemplateName(String templateName) {
this.templateName = templateName;
}
@Override
public void execute() {
final ListResponse<QuotaEmailTemplateResponse> response = new ListResponse<QuotaEmailTemplateResponse>();

View File

@ -88,7 +88,7 @@ public class QuotaEmailTemplateUpdateCmd extends BaseCmd {
return Account.ACCOUNT_ID_SYSTEM;
}
private void setTemplateName(String templateName) {
public void setTemplateName(String templateName) {
this.templateName = templateName;
}
@ -108,4 +108,15 @@ public class QuotaEmailTemplateUpdateCmd extends BaseCmd {
return locale;
}
public void setTemplateSubject(String templateSubject) {
this.templateSubject = templateSubject;
}
public void setTemplateBody(String templateBody) {
this.templateBody = templateBody;
}
public void setLocale(String locale) {
this.locale = locale;
}
}

View File

@ -298,16 +298,15 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
public QuotaCreditsResponse addQuotaCredits(final Long accountId, final Long domainId, final Double amount, final Long updatedBy, final Date despositedOn) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
QuotaCreditsVO result = null;
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
try {
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy);
s_logger.info("addQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn);
credits.setUpdatedOn(despositedOn);
result = _quotaCreditsDao.saveCredits(credits);
} finally {
txn.close();
}
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
final AccountVO account = _accountDao.findById(accountId);
final boolean lockAccountEnforcement = "true".equalsIgnoreCase(QuotaConfig.QuotaEnableEnforcement.value());
final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(despositedOn));
@ -372,16 +371,15 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@Override
public QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate) {
if (quotaBalance == null || quotaBalance.size() == 0) {
new InvalidParameterValueException("There are no balance entries on or before the requested date.");
throw new InvalidParameterValueException("There are no balance entries on or before the requested date.");
}
if (startDate == null) {
startDate = new Date();
}
QuotaBalanceResponse resp = new QuotaBalanceResponse();
BigDecimal lastCredits = new BigDecimal(0);
for (Iterator<QuotaBalanceVO> it = quotaBalance.iterator(); it.hasNext();) {
QuotaBalanceVO entry = it.next();
if (s_logger.isDebugEnabled()){
for (QuotaBalanceVO entry : quotaBalance) {
if (s_logger.isDebugEnabled()) {
s_logger.info("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId());
}
lastCredits = lastCredits.add(entry.getCreditBalance());

View File

@ -150,11 +150,10 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
Account userAccount = null;
Account caller = CallContext.current().getCallingAccount();
// if accountId is not specified, use accountName and domainId
if ((accountId == null) && (accountName != null) && (domainId != null)) {
Account userAccount = null;
Account caller = CallContext.current().getCallingAccount();
if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
@ -211,18 +210,17 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
}
}
}
@Override
public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
Account userAccount = null;
Account caller = CallContext.current().getCallingAccount();
// if accountId is not specified, use accountName and domainId
if ((accountId == null) && (accountName != null) && (domainId != null)) {
Account userAccount = null;
Account caller = CallContext.current().getCallingAccount();
if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);

View File

@ -0,0 +1,65 @@
// 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.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.QuotaBalanceResponse;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaBalanceCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Test
public void testQuotaBalanceCmd() throws NoSuchFieldException, IllegalAccessException {
QuotaBalanceCmd cmd = new QuotaBalanceCmd();
Field rbField = QuotaBalanceCmd.class.getDeclaredField("_responseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
List<QuotaBalanceVO> quotaBalanceVOList = new ArrayList<QuotaBalanceVO>();
Mockito.when(responseBuilder.getQuotaBalance(Mockito.any(cmd.getClass()))).thenReturn(quotaBalanceVOList);
Mockito.when(responseBuilder.createQuotaLastBalanceResponse(Mockito.eq(quotaBalanceVOList), Mockito.any(Date.class))).thenReturn(new QuotaBalanceResponse());
Mockito.when(responseBuilder.createQuotaBalanceResponse(Mockito.eq(quotaBalanceVOList), Mockito.any(Date.class), Mockito.any(Date.class))).thenReturn(new QuotaBalanceResponse());
Mockito.when(responseBuilder.startOfNextDay(Mockito.any(Date.class))).thenReturn(new Date());
// end date not specified
cmd.setStartDate(new Date());
cmd.setEndDate(null);
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaLastBalanceResponse(Mockito.eq(quotaBalanceVOList), Mockito.any(Date.class));
Mockito.verify(responseBuilder, Mockito.times(0)).createQuotaBalanceResponse(Mockito.eq(quotaBalanceVOList), Mockito.any(Date.class), Mockito.any(Date.class));
// end date specified
cmd.setEndDate(new Date());
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaBalanceResponse(Mockito.eq(quotaBalanceVOList), Mockito.any(Date.class), Mockito.any(Date.class));
}
}

View File

@ -0,0 +1,84 @@
// 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.api.command;
import com.cloud.user.AccountService;
import com.cloud.user.AccountVO;
import junit.framework.TestCase;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.QuotaCreditsResponse;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.quota.QuotaService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
@RunWith(MockitoJUnitRunner.class)
public class QuotaCreditsCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Mock
QuotaService quotaService;
@Mock
AccountService accountService;
@Test
public void testQuotaCreditsCmd() throws NoSuchFieldException, IllegalAccessException {
QuotaCreditsCmd cmd = new QuotaCreditsCmd();
cmd.setAccountName("admin");
Field rbField = QuotaCreditsCmd.class.getDeclaredField("_responseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
Field qsbField = QuotaCreditsCmd.class.getDeclaredField("_quotaService");
qsbField.setAccessible(true);
qsbField.set(cmd, quotaService);
Field asField = BaseCmd.class.getDeclaredField("_accountService");
asField.setAccessible(true);
asField.set(cmd, accountService);
AccountVO acc = new AccountVO();
acc.setId(2L);
Mockito.when(accountService.getActiveAccountByName(Mockito.anyString(), Mockito.anyLong())).thenReturn(acc);
Mockito.when(responseBuilder.addQuotaCredits(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyDouble(), Mockito.anyLong())).thenReturn(new QuotaCreditsResponse());
// No value provided test
try {
cmd.execute();
} catch (ServerApiException e) {
assertTrue(e.getErrorCode().equals(ApiErrorCode.PARAM_ERROR));
}
// With value provided test
cmd.setValue(11.80);
cmd.execute();
Mockito.verify(quotaService, Mockito.times(0)).setLockAccount(Mockito.anyLong(), Mockito.anyBoolean());
Mockito.verify(quotaService, Mockito.times(1)).setMinBalance(Mockito.anyLong(), Mockito.anyDouble());
Mockito.verify(responseBuilder, Mockito.times(1)).addQuotaCredits(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyDouble(), Mockito.anyLong());
}
}

View File

@ -0,0 +1,50 @@
// 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.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.QuotaEmailTemplateResponse;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaEmailTemplateListCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Test
public void testQuotaEmailTemplateListCmd() throws NoSuchFieldException, IllegalAccessException {
QuotaEmailTemplateListCmd cmd = new QuotaEmailTemplateListCmd();
Field rbField = QuotaEmailTemplateListCmd.class.getDeclaredField("_quotaResponseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
List<QuotaEmailTemplateResponse> responses = new ArrayList<QuotaEmailTemplateResponse>();
Mockito.when(responseBuilder.listQuotaEmailTemplates(Mockito.eq(cmd))).thenReturn(responses);
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).listQuotaEmailTemplates(cmd);
}
}

View File

@ -0,0 +1,68 @@
// 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.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
@RunWith(MockitoJUnitRunner.class)
public class QuotaEmailTemplateUpdateCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Test
public void testQuotaEmailTemplateUpdateCmd () throws NoSuchFieldException, IllegalAccessException {
QuotaEmailTemplateUpdateCmd cmd = new QuotaEmailTemplateUpdateCmd();
Field rbField = QuotaEmailTemplateUpdateCmd.class.getDeclaredField("_quotaResponseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
// templatename parameter check
try {
cmd.execute();
} catch (ServerApiException e) {
assertTrue(e.getErrorCode().equals(ApiErrorCode.PARAM_ERROR));
}
// invalid template name test
cmd.setTemplateName("randomTemplate");
cmd.setTemplateBody("some body");
cmd.setTemplateSubject("some subject");
try {
cmd.execute();
} catch (ServerApiException e) {
assertTrue(e.getErrorCode().equals(ApiErrorCode.PARAM_ERROR));
}
// valid template test
cmd.setTemplateName(QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY.toString());
Mockito.when(responseBuilder.updateQuotaEmailTemplate(Mockito.eq(cmd))).thenReturn(true);
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).updateQuotaEmailTemplate(Mockito.eq(cmd));
}
}

View File

@ -0,0 +1,53 @@
// 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.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.api.response.QuotaStatementResponse;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaStatementCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Test
public void testQuotaStatementCmd() throws NoSuchFieldException, IllegalAccessException {
QuotaStatementCmd cmd = new QuotaStatementCmd();
cmd.setAccountName("admin");
Field rbField = QuotaStatementCmd.class.getDeclaredField("_responseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
List<QuotaUsageVO> quotaUsageVOList = new ArrayList<QuotaUsageVO>();
Mockito.when(responseBuilder.getQuotaUsage(Mockito.eq(cmd))).thenReturn(quotaUsageVOList);
Mockito.when(responseBuilder.createQuotaStatementResponse(Mockito.eq(quotaUsageVOList))).thenReturn(new QuotaStatementResponse());
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).getQuotaUsage(Mockito.eq(cmd));
}
}

View File

@ -0,0 +1,62 @@
// 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.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.api.response.QuotaTariffResponse;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaTariffListCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Test
public void testQuotaTariffListCmd() throws NoSuchFieldException, IllegalAccessException {
QuotaTariffListCmd cmd = new QuotaTariffListCmd();
Field rbField = QuotaTariffListCmd.class.getDeclaredField("_responseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
List<QuotaTariffVO> quotaTariffVOList = new ArrayList<QuotaTariffVO>();
QuotaTariffVO tariff = new QuotaTariffVO();
tariff.setEffectiveOn(new Date());
tariff.setCurrencyValue(new BigDecimal(100));
tariff.setUsageType(QuotaTypes.MEMORY);
quotaTariffVOList.add(new QuotaTariffVO());
Mockito.when(responseBuilder.listQuotaTariffPlans(Mockito.eq(cmd))).thenReturn(quotaTariffVOList);
Mockito.when(responseBuilder.createQuotaTariffResponse(Mockito.any(QuotaTariffVO.class))).thenReturn(new QuotaTariffResponse());
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaTariffResponse(Mockito.any(QuotaTariffVO.class));
}
}

View File

@ -0,0 +1,67 @@
// 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.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.api.response.QuotaTariffResponse;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Date;
@RunWith(MockitoJUnitRunner.class)
public class QuotaTariffUpdateCmdTest extends TestCase {
@Mock
QuotaResponseBuilder responseBuilder;
@Test
public void testQuotaTariffUpdateCmd() throws NoSuchFieldException, IllegalAccessException {
QuotaTariffUpdateCmd cmd = new QuotaTariffUpdateCmd();
Field rbField = QuotaTariffUpdateCmd.class.getDeclaredField("_responseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
QuotaTariffVO tariff = new QuotaTariffVO();
tariff.setEffectiveOn(new Date());
tariff.setCurrencyValue(new BigDecimal(100));
tariff.setUsageType(QuotaTypes.MEMORY);
Mockito.when(responseBuilder.updateQuotaTariffPlan(Mockito.eq(cmd))).thenReturn(null);
try {
cmd.execute();
} catch (ServerApiException e) {
assertTrue(e.getErrorCode().equals(ApiErrorCode.INTERNAL_ERROR));
}
Mockito.when(responseBuilder.updateQuotaTariffPlan(Mockito.eq(cmd))).thenReturn(tariff);
Mockito.when(responseBuilder.createQuotaTariffResponse(Mockito.eq(tariff))).thenReturn(new QuotaTariffResponse());
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).createQuotaTariffResponse(Mockito.eq(tariff));
}
}

View File

@ -0,0 +1,228 @@
// 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.api.response;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.db.TransactionLegacy;
import junit.framework.TestCase;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.quota.QuotaService;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.region.RegionManager;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaResponseBuilderImplTest extends TestCase {
@Mock
QuotaTariffDao quotaTariffDao;
@Mock
QuotaBalanceDao quotaBalanceDao;
@Mock
QuotaCreditsDao quotaCreditsDao;
@Mock
QuotaEmailTemplatesDao quotaEmailTemplateDao;
@Mock
UserDao userDao;
@Mock
QuotaService quotaService;
@Mock
AccountDao accountDao;
@Mock
RegionManager regionMgr;
QuotaResponseBuilderImpl quotaResponseBuilder = new QuotaResponseBuilderImpl();
@Before
public void setup() throws IllegalAccessException, NoSuchFieldException {
// Dummy transaction stack setup
TransactionLegacy.open("QuotaResponseBuilderImplTest");
Field tariffDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaTariffDao");
tariffDaoField.setAccessible(true);
tariffDaoField.set(quotaResponseBuilder, quotaTariffDao);
Field balanceDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaBalanceDao");
balanceDaoField.setAccessible(true);
balanceDaoField.set(quotaResponseBuilder, quotaBalanceDao);
Field quotaCreditsDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaCreditsDao");
quotaCreditsDaoField.setAccessible(true);
quotaCreditsDaoField.set(quotaResponseBuilder, quotaCreditsDao);
Field quotaEmailTemplateDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaEmailTemplateDao");
quotaEmailTemplateDaoField.setAccessible(true);
quotaEmailTemplateDaoField.set(quotaResponseBuilder, quotaEmailTemplateDao);
Field userDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_userDao");
userDaoField.setAccessible(true);
userDaoField.set(quotaResponseBuilder, userDao);
Field quotaServiceField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaService");
quotaServiceField.setAccessible(true);
quotaServiceField.set(quotaResponseBuilder, quotaService);
Field accountDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_accountDao");
accountDaoField.setAccessible(true);
accountDaoField.set(quotaResponseBuilder, accountDao);
Field regionMgrField = QuotaResponseBuilderImpl.class.getDeclaredField("_regionMgr");
regionMgrField.setAccessible(true);
regionMgrField.set(quotaResponseBuilder, regionMgr);
}
private QuotaTariffVO makeTariffTestData() {
QuotaTariffVO tariffVO = new QuotaTariffVO();
tariffVO.setUsageType(QuotaTypes.IP_ADDRESS);
tariffVO.setUsageName("ip address");
tariffVO.setUsageUnit("IP-Month");
tariffVO.setCurrencyValue(new BigDecimal(100.19));
tariffVO.setEffectiveOn(new Date());
tariffVO.setUsageDiscriminator("");
return tariffVO;
}
@Test
public void testQuotaResponse() {
QuotaTariffVO tariffVO = makeTariffTestData();
QuotaTariffResponse response = quotaResponseBuilder.createQuotaTariffResponse(tariffVO);
assertTrue(tariffVO.getUsageType() == response.getUsageType());
assertTrue(tariffVO.getCurrencyValue().equals(response.getTariffValue()));
}
@Test
public void testAddQuotaCredits() {
final long accountId = 2L;
final long domainId = 2L;
final double amount = 11.0;
final long updatedBy = 2L;
final Date now = new Date();
QuotaCreditsVO credit = new QuotaCreditsVO();
credit.setCredit(new BigDecimal(amount));
Mockito.when(quotaCreditsDao.saveCredits(Mockito.any(QuotaCreditsVO.class))).thenReturn(credit);
Mockito.when(quotaBalanceDao.lastQuotaBalance(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(Date.class))).thenReturn(new BigDecimal(111));
AccountVO account = new AccountVO();
account.setState(Account.State.locked);
Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(account);
QuotaCreditsResponse resp = quotaResponseBuilder.addQuotaCredits(accountId, domainId, amount, updatedBy, now);
assertTrue(resp.getCredits().compareTo(credit.getCredit()) == 0);
}
@Test
public void testListQuotaEmailTemplates() {
QuotaEmailTemplateListCmd cmd = new QuotaEmailTemplateListCmd();
cmd.setTemplateName("some name");
List<QuotaEmailTemplatesVO> templates = new ArrayList<>();
QuotaEmailTemplatesVO template = new QuotaEmailTemplatesVO();
template.setTemplateName("template");
templates.add(template);
Mockito.when(quotaEmailTemplateDao.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(templates);
assertTrue(quotaResponseBuilder.listQuotaEmailTemplates(cmd).size() == 1);
}
@Test
public void testUpdateQuotaEmailTemplate() {
QuotaEmailTemplateUpdateCmd cmd = new QuotaEmailTemplateUpdateCmd();
cmd.setTemplateBody("some body");
cmd.setTemplateName("some name");
cmd.setTemplateSubject("some subject");
List<QuotaEmailTemplatesVO> templates = new ArrayList<>();
Mockito.when(quotaEmailTemplateDao.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(templates);
Mockito.when(quotaEmailTemplateDao.updateQuotaEmailTemplate(Mockito.any(QuotaEmailTemplatesVO.class))).thenReturn(true);
// invalid template test
assertFalse(quotaResponseBuilder.updateQuotaEmailTemplate(cmd));
// valid template test
QuotaEmailTemplatesVO template = new QuotaEmailTemplatesVO();
template.setTemplateName("template");
templates.add(template);
assertTrue(quotaResponseBuilder.updateQuotaEmailTemplate(cmd));
}
@Test
public void testCreateQuotaLastBalanceResponse() {
List<QuotaBalanceVO> quotaBalance = new ArrayList<>();
// null balance test
try {
quotaResponseBuilder.createQuotaLastBalanceResponse(null, new Date());
} catch (InvalidParameterValueException e) {
assertTrue(e.getMessage().equals("There are no balance entries on or before the requested date."));
}
// empty balance test
try {
quotaResponseBuilder.createQuotaLastBalanceResponse(quotaBalance, new Date());
} catch (InvalidParameterValueException e) {
assertTrue(e.getMessage().equals("There are no balance entries on or before the requested date."));
}
// valid balance test
QuotaBalanceVO entry = new QuotaBalanceVO();
entry.setAccountId(2L);
entry.setCreditBalance(new BigDecimal(100));
quotaBalance.add(entry);
quotaBalance.add(entry);
Mockito.when(quotaService.computeAdjustedTime(Mockito.any(Date.class))).thenReturn(new Date());
QuotaBalanceResponse resp = quotaResponseBuilder.createQuotaLastBalanceResponse(quotaBalance, null);
assertTrue(resp.getStartQuota().compareTo(new BigDecimal(200)) == 0);
}
@Test
public void testStartOfNextDay() {
DateTime now = new DateTime();
DateTime nextDay = new DateTime(quotaResponseBuilder.startOfNextDay(now.toDate()));
DateTime nextDay2 = new DateTime(quotaResponseBuilder.startOfNextDay());
assertTrue(now.toLocalDate().equals(nextDay.minusDays(1).toLocalDate()));
assertTrue(now.toLocalDate().equals(nextDay2.minusDays(1).toLocalDate()));
}
}

View File

@ -0,0 +1,184 @@
// 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 com.cloud.configuration.Config;
import com.cloud.domain.dao.DomainDao;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.db.TransactionLegacy;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
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.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import javax.naming.ConfigurationException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaServiceImplTest extends TestCase {
@Mock
AccountDao accountDao;
@Mock
QuotaAccountDao quotaAcc;
@Mock
QuotaUsageDao quotaUsageDao;
@Mock
DomainDao domainDao;
@Mock
ConfigurationDao configDao;
@Mock
QuotaBalanceDao quotaBalanceDao;
@Mock
QuotaResponseBuilder respBldr;
QuotaServiceImpl quotaService = new QuotaServiceImpl();
@Before
public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
// Dummy transaction stack setup
TransactionLegacy.open("QuotaServiceImplTest");
Field accountDaoField = QuotaServiceImpl.class.getDeclaredField("_accountDao");
accountDaoField.setAccessible(true);
accountDaoField.set(quotaService, accountDao);
Field quotaAccountDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaAcc");
quotaAccountDaoField.setAccessible(true);
quotaAccountDaoField.set(quotaService, quotaAcc);
Field quotaUsageDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaUsageDao");
quotaUsageDaoField.setAccessible(true);
quotaUsageDaoField.set(quotaService, quotaUsageDao);
Field domainDaoField = QuotaServiceImpl.class.getDeclaredField("_domainDao");
domainDaoField.setAccessible(true);
domainDaoField.set(quotaService, domainDao);
Field configDaoField = QuotaServiceImpl.class.getDeclaredField("_configDao");
configDaoField.setAccessible(true);
configDaoField.set(quotaService, configDao);
Field balanceDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaBalanceDao");
balanceDaoField.setAccessible(true);
balanceDaoField.set(quotaService, quotaBalanceDao);
Field QuotaResponseBuilderField = QuotaServiceImpl.class.getDeclaredField("_respBldr");
QuotaResponseBuilderField.setAccessible(true);
QuotaResponseBuilderField.set(quotaService, respBldr);
Mockito.when(configDao.getValue(Mockito.eq(Config.UsageAggregationTimezone.toString()))).thenReturn("IST");
Mockito.when(configDao.getValue(Mockito.eq(Config.UsageStatsJobAggregationRange.toString()))).thenReturn("1");
quotaService.configure("randomName", null);
}
@Test
public void testComputeAdjustedTime() {
DateTime now = new DateTime(DateTimeZone.UTC);
DateTime result = new DateTime(quotaService.computeAdjustedTime(now.toDate())); // IST
// Jodatime's compareTo counts for the the different timezones
assertTrue(now.compareTo(result) == 0);
}
@Test
public void testFindQuotaBalanceVO() {
final long accountId = 2L;
final String accountName = "admin123";
final long domainId = 1L;
final Date startDate = new DateTime().minusDays(2).toDate();
final Date endDate = new Date();
List<QuotaBalanceVO> records = new ArrayList<>();
QuotaBalanceVO qb = new QuotaBalanceVO();
qb.setCreditBalance(new BigDecimal(100));
qb.setAccountId(accountId);
records.add(qb);
Mockito.when(respBldr.startOfNextDay()).thenReturn(endDate);
Mockito.when(respBldr.startOfNextDay(Mockito.any(Date.class))).thenReturn(startDate);
Mockito.when(quotaBalanceDao.findQuotaBalance(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.any(Date.class), Mockito.any(Date.class))).thenReturn(records);
Mockito.when(quotaBalanceDao.lastQuotaBalanceVO(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.any(Date.class))).thenReturn(records);
// with enddate
assertTrue(quotaService.findQuotaBalanceVO(accountId, accountName, domainId, startDate, endDate).get(0).equals(qb));
// without enddate
assertTrue(quotaService.findQuotaBalanceVO(accountId, accountName, domainId, startDate, null).get(0).equals(qb));
}
@Test
public void testGetQuotaUsage() {
final long accountId = 2L;
final String accountName = "admin123";
final long domainId = 1L;
final Date startDate = new DateTime().minusDays(2).toDate();
final Date endDate = new Date();
Mockito.when(respBldr.startOfNextDay()).thenReturn(endDate);
quotaService.getQuotaUsage(accountId, accountName, domainId, QuotaTypes.IP_ADDRESS, startDate, endDate);
Mockito.verify(quotaUsageDao, Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(Date.class), Mockito.any(Date.class));
}
@Test
public void testSetLockAccount() {
// existing account
QuotaAccountVO quotaAccountVO = new QuotaAccountVO();
Mockito.when(quotaAcc.findById(Mockito.anyLong())).thenReturn(quotaAccountVO);
quotaService.setLockAccount(2L, true);
Mockito.verify(quotaAcc, Mockito.times(0)).persist(Mockito.any(QuotaAccountVO.class));
Mockito.verify(quotaAcc, Mockito.times(1)).update(Mockito.anyLong(), Mockito.any(QuotaAccountVO.class));
// new account
Mockito.when(quotaAcc.findById(Mockito.anyLong())).thenReturn(null);
quotaService.setLockAccount(2L, true);
Mockito.verify(quotaAcc, Mockito.times(1)).persist(Mockito.any(QuotaAccountVO.class));
}
@Test
public void testSetMinBalance() {
final long accountId = 2L;
final double balance = 10.3F;
// existing account setting
QuotaAccountVO quotaAccountVO = new QuotaAccountVO();
Mockito.when(quotaAcc.findById(Mockito.anyLong())).thenReturn(quotaAccountVO);
quotaService.setMinBalance(accountId, balance);
Mockito.verify(quotaAcc, Mockito.times(0)).persist(Mockito.any(QuotaAccountVO.class));
Mockito.verify(quotaAcc, Mockito.times(1)).update(Mockito.anyLong(), Mockito.any(QuotaAccountVO.class));
// no account with limit set
Mockito.when(quotaAcc.findById(Mockito.anyLong())).thenReturn(null);
quotaService.setMinBalance(accountId, balance);
Mockito.verify(quotaAcc, Mockito.times(1)).persist(Mockito.any(QuotaAccountVO.class));
}
}

View File

@ -62,6 +62,9 @@ public class ParamProcessWorkerTest {
@Parameter(name = "boolparam1", type = CommandType.BOOLEAN)
boolean boolparam1;
@Parameter(name = "doubleparam1", type = CommandType.DOUBLE)
double doubleparam1;
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException {
@ -98,10 +101,12 @@ public class ParamProcessWorkerTest {
params.put("strparam1", "foo");
params.put("intparam1", "100");
params.put("boolparam1", "true");
params.put("doubleparam1", "11.89");
final TestCmd cmd = new TestCmd();
paramProcessWorker.processParameters(cmd, params);
Assert.assertEquals("foo", cmd.strparam1);
Assert.assertEquals(100, cmd.intparam1);
Assert.assertTrue(Double.compare(cmd.doubleparam1, 11.89) == 0);
}
}

View File

@ -396,7 +396,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
}
try {
_alertManager.checkAndSendQuotaAlertEmails();
_alertManager.sendMonthlyStatement();
_alertManager.sendMonthlyStatement(new Date());
} catch (Exception e) {
s_logger.fatal("Exception received while sending alerts " + e.getMessage());
if (s_logger.isDebugEnabled()) {

View File

@ -18,10 +18,9 @@ package org.apache.cloudstack.quota;
import com.cloud.utils.component.Manager;
import java.util.Date;
public interface QuotaAlertManager extends Manager {
public void checkAndSendQuotaAlertEmails();
void sendMonthlyStatement();
void checkAndSendQuotaAlertEmails();
void sendMonthlyStatement(Date now);
}

View File

@ -19,9 +19,9 @@ package org.apache.cloudstack.quota;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.user.Account;
import com.cloud.user.Account.State;
import com.cloud.user.AccountVO;
import com.cloud.user.UserVO;
import com.cloud.user.Account.State;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.DateUtil;
@ -32,7 +32,6 @@ import com.cloud.utils.exception.CloudRuntimeException;
import com.sun.mail.smtp.SMTPMessage;
import com.sun.mail.smtp.SMTPSSLTransport;
import com.sun.mail.smtp.SMTPTransport;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
@ -55,7 +54,6 @@ import javax.mail.Session;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.naming.ConfigurationException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
@ -150,8 +148,7 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
@SuppressWarnings("deprecation")
@Override
public void sendMonthlyStatement() {
Date now = new Date();
public void sendMonthlyStatement(Date now) {
Calendar aCalendar = Calendar.getInstance();
aCalendar.add(Calendar.MONTH, -1);
aCalendar.set(Calendar.DATE, 1);
@ -170,22 +167,17 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
Date lastStatementDate = quotaAccount.getLastStatementDate();
if (now.getDate() < 6) {
AccountVO account = _accountDao.findById(quotaAccount.getId());
if (lastStatementDate == null) {
if (lastStatementDate == null || getDifferenceDays(lastStatementDate, new Date()) >= 7) {
BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
// send statement
deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
} else if (getDifferenceDays(lastStatementDate, new Date()) < 7) {
s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
} else {
BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, firstDateOfPreviousMonth, lastDateOfPreviousMonth);
s_logger.info("For account=" + quotaAccount.getId() + ", quota used = " + quotaUsage);
deferredQuotaEmailList.add(new DeferredQuotaEmail(account, quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
s_logger.debug("For " + quotaAccount.getId() + " the statement has been sent recently");
}
} else {
s_logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) + " days, will send statement in next cycle");
}
}
for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
@ -209,7 +201,7 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
if (accountBalance != null) {
AccountVO account = _accountDao.findById(quotaAccount.getId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Check id " + account.getId() + " bal=" + accountBalance + " alertDate" + alertDate + " diff" + getDifferenceDays(alertDate, new Date()));
s_logger.debug("Check id " + account.getId() + " bal=" + accountBalance + " alertDate" + alertDate + " current date" + new Date());
}
if (accountBalance.compareTo(zeroBalance) <= 0) {
if (_lockAccountEnforcement && (lockable == 1)) {
@ -234,7 +226,7 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
}
}
private void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
public void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
final AccountVO account = emailToBeSent.getAccount();
final BigDecimal balance = emailToBeSent.getQuotaBalance();
final BigDecimal usage = emailToBeSent.getQuotaUsage();
@ -278,7 +270,7 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
final String body = templateEngine.replace(emailTemplate.getTemplateBody());
try {
_emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body);
emailToBeSent.sentSuccessfully();
emailToBeSent.sentSuccessfully(_quotaAcc);
} catch (Exception e) {
s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
account.getUuid(), emailRecipients, e));
@ -288,7 +280,36 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
}
}
class DeferredQuotaEmail {
public static long getDifferenceDays(Date d1, Date d2) {
long diff = d2.getTime() - d1.getTime();
return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
protected boolean lockAccount(long accountId) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
boolean success = false;
Account account = _accountDao.findById(accountId);
if (account != null) {
if (account.getState().equals(State.locked)) {
return true; // already locked, no-op
} else if (account.getState().equals(State.enabled)) {
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.locked);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
} else {
if (s_logger.isInfoEnabled()) {
s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
}
}
} else {
s_logger.warn("Failed to lock account " + accountId + ", account not found.");
}
TransactionLegacy.open(opendb).close();
return success;
}
public static class DeferredQuotaEmail {
private AccountVO account;
private QuotaAccountVO quotaAccount;
private QuotaConfig.QuotaEmailTemplateTypes emailTemplateType;
@ -324,15 +345,14 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
return emailTemplateType;
}
public void sentSuccessfully() {
public void sentSuccessfully(final QuotaAccountDao quotaAccountDao) {
if (emailTemplateType == QuotaEmailTemplateTypes.QUOTA_STATEMENT) {
quotaAccount.setLastStatementDate(new Date());
_quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
} else {
quotaAccount.setQuotaAlertDate(new Date());
quotaAccount.setQuotaAlertType(emailTemplateType.ordinal());
_quotaAcc.update(quotaAccount.getAccountId(), quotaAccount);
}
quotaAccountDao.update(quotaAccount.getAccountId(), quotaAccount);
}
};
@ -421,54 +441,4 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
}
}
}
public Date startOfNextDay() {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.DATE, 1);
Date dt = c.getTime();
return dt;
}
public static long getDifferenceDays(Date d1, Date d2) {
long diff = d2.getTime() - d1.getTime();
return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
protected boolean lockAccount(long accountId) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
boolean success = false;
Account account = _accountDao.findById(accountId);
if (account != null) {
if (account.getState().equals(State.locked)) {
return true; // already locked, no-op
} else if (account.getState().equals(State.enabled)) {
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.locked);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
} else {
if (s_logger.isInfoEnabled()) {
s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
}
}
} else {
s_logger.warn("Failed to lock account " + accountId + ", account not found.");
}
TransactionLegacy.open(opendb).close();
return success;
}
public boolean enableAccount(long accountId) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
boolean success = false;
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.enabled);
acctForUpdate.setNeedsCleanup(false);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
TransactionLegacy.open(opendb).close();
return success;
}
}

View File

@ -18,27 +18,24 @@ package org.apache.cloudstack.quota;
import com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.Account.State;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.TransactionLegacy;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
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.dao.ServiceOfferingDao;
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.quota.vo.ServiceOfferingVO;
import org.apache.cloudstack.quota.dao.ServiceOfferingDao;
import org.apache.cloudstack.utils.usage.UsageUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@ -46,11 +43,9 @@ import org.springframework.stereotype.Component;
import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -142,122 +137,130 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
return true;
}
public List<QuotaUsageVO> aggregatePendingQuotaRecordsForAccount(final AccountVO account, final Pair<List<? extends UsageVO>, Integer> usageRecords) {
List<QuotaUsageVO> quotaListForAccount = new ArrayList<>();
if (usageRecords == null || usageRecords.first() == null || usageRecords.first().isEmpty()) {
return quotaListForAccount;
}
s_logger.info("Getting pending quota records for account=" + account.getAccountName());
for (UsageVO usageRecord: usageRecords.first()) {
BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
switch (usageRecord.getUsageType()) {
case QuotaTypes.RUNNING_VM:
quotaListForAccount.addAll(updateQuotaRunningVMUsage(usageRecord, aggregationRatio));
break;
case QuotaTypes.ALLOCATED_VM:
quotaListForAccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
break;
case QuotaTypes.SNAPSHOT:
case QuotaTypes.TEMPLATE:
case QuotaTypes.ISO:
case QuotaTypes.VOLUME:
case QuotaTypes.VM_SNAPSHOT:
quotaListForAccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, usageRecord.getUsageType()));
break;
case QuotaTypes.LOAD_BALANCER_POLICY:
case QuotaTypes.PORT_FORWARDING_RULE:
case QuotaTypes.IP_ADDRESS:
case QuotaTypes.NETWORK_OFFERING:
case QuotaTypes.SECURITY_GROUP:
case QuotaTypes.VPN_USERS:
quotaListForAccount.add(updateQuotaRaw(usageRecord, aggregationRatio, usageRecord.getUsageType()));
break;
case QuotaTypes.NETWORK_BYTES_RECEIVED:
case QuotaTypes.NETWORK_BYTES_SENT:
quotaListForAccount.add(updateQuotaNetwork(usageRecord, usageRecord.getUsageType()));
break;
case QuotaTypes.VM_DISK_IO_READ:
case QuotaTypes.VM_DISK_IO_WRITE:
case QuotaTypes.VM_DISK_BYTES_READ:
case QuotaTypes.VM_DISK_BYTES_WRITE:
default:
break;
}
}
return quotaListForAccount;
}
public void processQuotaBalanceForAccount(final AccountVO account, final List<QuotaUsageVO> quotaListForAccount) {
if (quotaListForAccount == null || quotaListForAccount.size() < 1) {
return;
}
quotaListForAccount.add(new QuotaUsageVO());
Date startDate = quotaListForAccount.get(0).getStartDate();
Date endDate = quotaListForAccount.get(0).getEndDate();
BigDecimal aggrUsage = new BigDecimal(0);
for (QuotaUsageVO entry: quotaListForAccount) {
if (startDate.compareTo(entry.getStartDate()) != 0) {
QuotaBalanceVO lastRealBalanceEntry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
Date lastBalanceDate = new Date(0);
if (lastRealBalanceEntry != null) {
lastBalanceDate = lastRealBalanceEntry.getUpdatedOn();
aggrUsage = aggrUsage.add(lastRealBalanceEntry.getCreditBalance());
}
List<QuotaBalanceVO> creditsReceived = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastBalanceDate, endDate);
if (creditsReceived != null) {
for (QuotaBalanceVO credit : creditsReceived) {
aggrUsage = aggrUsage.add(credit.getCreditBalance());
}
}
QuotaBalanceVO newBalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Balance entry=" + aggrUsage + " on Date=" + endDate);
}
_quotaBalanceDao.persist(newBalance);
aggrUsage = new BigDecimal(0);
}
startDate = entry.getStartDate();
endDate = entry.getEndDate();
aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
}
// update quota_accounts
QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Updating quota account bal=" + aggrUsage + " date=" + endDate);
}
if (quota_account == null) {
quota_account = new QuotaAccountVO(account.getAccountId());
quota_account.setQuotaBalance(aggrUsage);
quota_account.setQuotaBalanceDate(endDate);
_quotaAcc.persist(quota_account);
} else {
quota_account.setQuotaBalance(aggrUsage);
quota_account.setQuotaBalanceDate(endDate);
_quotaAcc.update(account.getAccountId(), quota_account);
}
}
@Override
public boolean calculateQuotaUsage() {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
boolean jobResult = false;
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
try {
// get all the active accounts for which there is usage
try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) {
List<AccountVO> accounts = _accountDao.listAll();
for (AccountVO account : accounts) { // START ACCOUNT
Pair<List<? extends UsageVO>, Integer> usageRecords = null;
List<QuotaUsageVO> quotalistforaccount = new ArrayList<QuotaUsageVO>();
do {
s_logger.info("Account =" + account.getAccountName());
usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
s_logger.debug("Usage records found " + usageRecords.second());
for (UsageVO usageRecord : usageRecords.first()) {
BigDecimal aggregationRatio = new BigDecimal(_aggregationDuration).divide(s_minutesInMonth, 8, RoundingMode.HALF_EVEN);
switch (usageRecord.getUsageType()) {
case QuotaTypes.RUNNING_VM:
case QuotaTypes.ALLOCATED_VM:
quotalistforaccount.add(updateQuotaAllocatedVMUsage(usageRecord, aggregationRatio));
break;
case QuotaTypes.SNAPSHOT:
case QuotaTypes.TEMPLATE:
case QuotaTypes.ISO:
case QuotaTypes.VOLUME:
case QuotaTypes.VM_SNAPSHOT:
quotalistforaccount.add(updateQuotaDiskUsage(usageRecord, aggregationRatio, usageRecord.getUsageType()));
break;
case QuotaTypes.LOAD_BALANCER_POLICY:
case QuotaTypes.PORT_FORWARDING_RULE:
case QuotaTypes.IP_ADDRESS:
case QuotaTypes.NETWORK_OFFERING:
case QuotaTypes.SECURITY_GROUP:
case QuotaTypes.VPN_USERS:
quotalistforaccount.add(updateQuotaRaw(usageRecord, aggregationRatio, usageRecord.getUsageType()));
break;
case QuotaTypes.NETWORK_BYTES_RECEIVED:
case QuotaTypes.NETWORK_BYTES_SENT:
quotalistforaccount.add(updateQuotaNetwork(usageRecord, usageRecord.getUsageType()));
break;
case QuotaTypes.VM_DISK_IO_READ:
case QuotaTypes.VM_DISK_IO_WRITE:
case QuotaTypes.VM_DISK_BYTES_READ:
case QuotaTypes.VM_DISK_BYTES_WRITE:
default:
break;
}
}
} while ((usageRecords != null) && !usageRecords.first().isEmpty());
// list of quotas for this account
for (AccountVO account : accounts) {
Pair<List<? extends UsageVO>, Integer> usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), 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());
s_logger.debug("Quota entries size = " + quotaListForAccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
}
if (quotalistforaccount.size() > 0) { // balance to be processed
quotalistforaccount.add(new QuotaUsageVO());
Date startDate = quotalistforaccount.get(0).getStartDate();
Date endDate = quotalistforaccount.get(0).getEndDate();
BigDecimal aggrUsage = new BigDecimal(0);
for (QuotaUsageVO entry : quotalistforaccount) {
if (startDate.compareTo(entry.getStartDate()) != 0) {
QuotaBalanceVO lastrealbalanceentry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), startDate);
Date lastbalancedate;
if (lastrealbalanceentry != null) {
lastbalancedate = lastrealbalanceentry.getUpdatedOn();
aggrUsage = aggrUsage.add(lastrealbalanceentry.getCreditBalance());
} else {
lastbalancedate = new Date(0);
}
List<QuotaBalanceVO> creditsrcvd = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), lastbalancedate, endDate);
for (QuotaBalanceVO credit : creditsrcvd) {
aggrUsage = aggrUsage.add(credit.getCreditBalance());
}
QuotaBalanceVO newbalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Balance entry=" + aggrUsage + " on Date=" + endDate);
}
_quotaBalanceDao.persist(newbalance);
aggrUsage = new BigDecimal(0);
}
startDate = entry.getStartDate();
endDate = entry.getEndDate();
aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
}
// update is quota_accounts
QuotaAccountVO quota_account = _quotaAcc.findById(account.getAccountId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Updating quota account bal=" + aggrUsage + " date=" + endDate);
}
if (quota_account == null) {
quota_account = new QuotaAccountVO(account.getAccountId());
quota_account.setQuotaBalance(aggrUsage);
quota_account.setQuotaBalanceDate(endDate);
_quotaAcc.persist(quota_account);
} else {
quota_account.setQuotaBalance(aggrUsage);
quota_account.setQuotaBalanceDate(endDate);
_quotaAcc.update(account.getAccountId(), quota_account);
}
}// balance processed
} // END ACCOUNT
processQuotaBalanceForAccount(account, quotaListForAccount);
}
jobResult = true;
} catch (Exception e) {
s_logger.error("Quota Manager error", e);
} finally {
txn.close();
TransactionLegacy.open(opendb).close();
}
TransactionLegacy.open(opendb).close();
return jobResult;
}
@DB
private QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final BigDecimal aggregationRatio, final int quotaType) {
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)) {
@ -277,7 +280,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
}
@DB
private List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
public List<QuotaUsageVO> updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
List<QuotaUsageVO> quotalist = new ArrayList<QuotaUsageVO>();
QuotaUsageVO quota_usage;
BigDecimal cpuquotausgage, speedquotausage, memoryquotausage, vmusage;
@ -333,7 +336,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
}
@DB
private QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) {
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)) {
@ -352,7 +355,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
}
@DB
private QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final BigDecimal aggregationRatio, final int ruleType) {
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)) {
@ -371,7 +374,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
}
@DB
private QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
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)) {
@ -391,47 +394,4 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
return quota_usage;
}
public Date startOfNextDay() {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.DATE, 1);
Date dt = c.getTime();
return dt;
}
protected boolean lockAccount(long accountId) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
boolean success = false;
Account account = _accountDao.findById(accountId);
if (account != null) {
if (account.getState().equals(State.locked)) {
return true; // already locked, no-op
} else if (account.getState().equals(State.enabled)) {
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.locked);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
} else {
if (s_logger.isInfoEnabled()) {
s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
}
}
} else {
s_logger.warn("Failed to lock account " + accountId + ", account not found.");
}
TransactionLegacy.open(opendb).close();
return success;
}
public boolean enableAccount(long accountId) {
final short opendb = TransactionLegacy.currentTxn().getDatabaseId();
TransactionLegacy.open(TransactionLegacy.CLOUD_DB).close();
boolean success = false;
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.enabled);
acctForUpdate.setNeedsCleanup(false);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
TransactionLegacy.open(opendb).close();
return success;
}
}

View File

@ -0,0 +1,239 @@
// 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 com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.UserVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.db.TransactionLegacy;
import junit.framework.TestCase;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import javax.mail.MessagingException;
import javax.naming.ConfigurationException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaAlertManagerImplTest extends TestCase {
@Mock
AccountDao accountDao;
@Mock
QuotaAccountDao quotaAcc;
@Mock
UserDao userDao;
@Mock
DomainDao domainDao;
@Mock
QuotaEmailTemplatesDao quotaEmailTemplateDao;
@Mock
ConfigurationDao configDao;
@Mock
QuotaUsageDao quotaUsage;
@Mock
QuotaAlertManagerImpl.EmailQuotaAlert emailQuotaAlert;
@Spy
QuotaAlertManagerImpl quotaAlertManager = new QuotaAlertManagerImpl();
private void injectMockToField(Object mock, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field f = QuotaAlertManagerImpl.class.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(quotaAlertManager, mock);
}
@Before
public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
// Dummy transaction stack setup
TransactionLegacy.open("QuotaAlertManagerImplTest");
injectMockToField(accountDao, "_accountDao");
injectMockToField(quotaAcc, "_quotaAcc");
injectMockToField(userDao, "_userDao");
injectMockToField(domainDao, "_domainDao");
injectMockToField(quotaEmailTemplateDao, "_quotaEmailTemplateDao");
injectMockToField(configDao, "_configDao");
injectMockToField(quotaUsage, "_quotaUsage");
injectMockToField(emailQuotaAlert, "_emailQuotaAlert");
}
@Test
public void testStartStop() {
try {
quotaAlertManager.start(); // expected to fail as pid is not available
} catch (NumberFormatException ignored) {
}
assertTrue(quotaAlertManager.stop());
}
@Test
public void testSendMonthlyStatement() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO);
QuotaAccountVO acc = new QuotaAccountVO(2L);
acc.setQuotaBalance(new BigDecimal(404));
acc.setLastStatementDate(null);
List<QuotaAccountVO> accounts = new ArrayList<>();
accounts.add(acc);
Mockito.when(quotaAcc.listAll()).thenReturn(accounts);
Mockito.when(quotaUsage.findTotalQuotaUsage(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyInt(),
Mockito.any(Date.class), Mockito.any(Date.class))).thenReturn(new BigDecimal(100));
// Don't test sendQuotaAlert yet
Mockito.doNothing().when(quotaAlertManager).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
// call real method on send monthly statement
Mockito.doCallRealMethod().when(quotaAlertManager).sendMonthlyStatement(Mockito.any(Date.class));
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.set(Calendar.DATE, 1); // simulate sending statement on 1st of the month
Date nowDate = cal.getTime();
assertTrue(nowDate.getDate() < 6);
quotaAlertManager.sendMonthlyStatement(nowDate);
Mockito.verify(quotaAlertManager, Mockito.times(accounts.size())).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
}
@Test
public void testCheckAndSendQuotaAlertEmails() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
accountVO.setType(Account.ACCOUNT_TYPE_NORMAL);
Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO);
QuotaAccountVO acc = new QuotaAccountVO(2L);
acc.setQuotaBalance(new BigDecimal(404));
acc.setQuotaMinBalance(new BigDecimal(100));
acc.setQuotaBalanceDate(new Date());
acc.setQuotaAlertDate(null);
acc.setQuotaEnforce(0);
List<QuotaAccountVO> accounts = new ArrayList<>();
accounts.add(acc);
Mockito.when(quotaAcc.listAll()).thenReturn(accounts);
// Don't test sendQuotaAlert yet
Mockito.doNothing().when(quotaAlertManager).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
Mockito.doReturn(true).when(quotaAlertManager).lockAccount(Mockito.anyLong());
// call real method on send monthly statement
Mockito.doCallRealMethod().when(quotaAlertManager).checkAndSendQuotaAlertEmails();
// Case1: valid balance, no email should be sent
quotaAlertManager.checkAndSendQuotaAlertEmails();
Mockito.verify(quotaAlertManager, Mockito.times(0)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
// Case2: low balance, email should be sent
accounts.get(0).setQuotaBalance(new BigDecimal(99));
//Mockito.when(quotaAcc.listAll()).thenReturn(accounts);
quotaAlertManager.checkAndSendQuotaAlertEmails();
Mockito.verify(quotaAlertManager, Mockito.times(1)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
}
@Test
public void testSendQuotaAlert() throws UnsupportedEncodingException, MessagingException {
Mockito.doCallRealMethod().when(quotaAlertManager).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
AccountVO account = new AccountVO();
account.setId(2L);
account.setDomainId(1L);
account.setType(Account.ACCOUNT_TYPE_NORMAL);
account.setAccountName("admin");
account.setUuid("uuid");
QuotaAccountVO quotaAccount = new QuotaAccountVO(2L);
quotaAccount.setQuotaBalance(new BigDecimal(404));
quotaAccount.setQuotaMinBalance(new BigDecimal(100));
quotaAccount.setQuotaBalanceDate(new Date());
quotaAccount.setQuotaAlertDate(null);
quotaAccount.setQuotaEnforce(0);
QuotaAlertManagerImpl.DeferredQuotaEmail email = new QuotaAlertManagerImpl.DeferredQuotaEmail(account, quotaAccount, new BigDecimal(100), QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW);
QuotaEmailTemplatesVO quotaEmailTemplatesVO = new QuotaEmailTemplatesVO();
quotaEmailTemplatesVO.setTemplateSubject("Low quota");
quotaEmailTemplatesVO.setTemplateBody("Low quota {{accountID}}");
List<QuotaEmailTemplatesVO> emailTemplates = new ArrayList<>();
emailTemplates.add(quotaEmailTemplatesVO);
Mockito.when(quotaEmailTemplateDao.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(emailTemplates);
DomainVO domain = new DomainVO();
domain.setUuid("uuid");
domain.setName("/domain");
Mockito.when(domainDao.findByIdIncludingRemoved(Mockito.anyLong())).thenReturn(new DomainVO());
UserVO user = new UserVO();
user.setUsername("user1");
user.setEmail("user1@apache.org");
List<UserVO> users = new ArrayList<>();
users.add(user);
Mockito.when(userDao.listByAccount(Mockito.anyLong())).thenReturn(users);
quotaAlertManager.sendQuotaAlert(email);
Mockito.verify(emailQuotaAlert, Mockito.times(1)).sendQuotaAlert(Mockito.anyList(), Mockito.anyString(), Mockito.anyString());
}
@Test
public void testGetDifferenceDays() {
Date now = new Date();
assertTrue(QuotaAlertManagerImpl.getDifferenceDays(now, now) == 0L);
assertTrue(QuotaAlertManagerImpl.getDifferenceDays(now, new DateTime(now).plusDays(1).toDate()) == 1L);
}
@Test
public void testLockAccount() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
accountVO.setType(Account.ACCOUNT_TYPE_NORMAL);
accountVO.setState(Account.State.enabled);
Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO);
Mockito.when(accountDao.createForUpdate()).thenReturn(accountVO);
Mockito.when(accountDao.update(Mockito.eq(accountVO.getId()), Mockito.eq(accountVO))).thenReturn(true);
assertTrue(quotaAlertManager.lockAccount(accountVO.getId()));
}
}

View File

@ -0,0 +1,203 @@
// 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 com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.Pair;
import com.cloud.utils.db.TransactionLegacy;
import junit.framework.TestCase;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
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.dao.ServiceOfferingDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.usage.UsageTypes;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import javax.naming.ConfigurationException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(MockitoJUnitRunner.class)
public class QuotaManagerImplTest extends TestCase {
@Mock
private AccountDao accountDao;
@Mock
private QuotaAccountDao quotaAcc;
@Mock
private UsageDao usageDao;
@Mock
private QuotaTariffDao quotaTariffDao;
@Mock
private QuotaUsageDao quotaUsageDao;
@Mock
private ServiceOfferingDao serviceOfferingDao;
@Mock
private QuotaBalanceDao quotaBalanceDao;
@Mock
private ConfigurationDao configDao;
@Spy
QuotaManagerImpl quotaManager = new QuotaManagerImpl();
private void injectMockToField(Object mock, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field f = QuotaManagerImpl.class.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(quotaManager, mock);
}
@Before
public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
// Dummy transaction stack setup
TransactionLegacy.open("QuotaManagerImplTest");
injectMockToField(accountDao, "_accountDao");
injectMockToField(quotaAcc, "_quotaAcc");
injectMockToField(usageDao, "_usageDao");
injectMockToField(quotaTariffDao, "_quotaTariffDao");
injectMockToField(quotaUsageDao, "_quotaUsageDao");
injectMockToField(serviceOfferingDao, "_serviceOfferingDao");
injectMockToField(quotaBalanceDao, "_quotaBalanceDao");
injectMockToField(configDao, "_configDao");
}
@Test
public void testStartStop() {
try {
quotaManager.start(); // expected to fail as pid is not available
} catch (NumberFormatException ignored) {
}
assertTrue(quotaManager.stop());
}
@Test
public void testConfig() throws ConfigurationException {
Mockito.when(configDao.getConfiguration(Mockito.anyMapOf(String.class, Object.class))).thenReturn(new HashMap<String, String>());
Map<String, Object> map = new HashMap<>();
map.put("usage.stats.job.aggregation.range", "0");
assertTrue(quotaManager.configure("quotaManager", map));
}
@Test
public void testCalculateQuotaUsage() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
accountVO.setType(Account.ACCOUNT_TYPE_NORMAL);
List<AccountVO> accountVOList = new ArrayList<>();
accountVOList.add(accountVO);
Mockito.when(accountDao.listAll()).thenReturn(accountVOList);
UsageVO usageVO = new UsageVO();
usageVO.setQuotaCalculated(0);
List<UsageVO> usageVOList = new ArrayList<UsageVO>();
usageVOList.add(usageVO);
Pair<List<? extends UsageVO>, Integer> usageRecords = new Pair<List<? extends UsageVO>, Integer>(usageVOList, usageVOList.size());
Mockito.when(usageDao.getUsageRecordsPendingQuotaAggregation(Mockito.anyLong(), Mockito.anyLong())).thenReturn(usageRecords);
QuotaUsageVO quotaUsageVO = new QuotaUsageVO();
quotaUsageVO.setAccountId(2L);
List<QuotaUsageVO> quotaListForAccount = new ArrayList<>();
quotaListForAccount.add(quotaUsageVO);
Mockito.doReturn(quotaListForAccount).when(quotaManager).aggregatePendingQuotaRecordsForAccount(Mockito.eq(accountVO), Mockito.eq(usageRecords));
Mockito.doNothing().when(quotaManager).processQuotaBalanceForAccount(Mockito.eq(accountVO), Mockito.eq(quotaListForAccount));
assertTrue(quotaManager.calculateQuotaUsage());
}
@Test
public void testAggregatePendingQuotaRecordsForAccount() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
accountVO.setType(Account.ACCOUNT_TYPE_NORMAL);
UsageVO usageVO = new UsageVO();
usageVO.setQuotaCalculated(0);
usageVO.setUsageType(UsageTypes.ALLOCATED_VM);
List<UsageVO> usageVOList = new ArrayList<UsageVO>();
usageVOList.add(usageVO);
Pair<List<? extends UsageVO>, Integer> usageRecords = new Pair<List<? extends UsageVO>, Integer>(usageVOList, usageVOList.size());
QuotaUsageVO quotaUsageVO = new QuotaUsageVO();
quotaUsageVO.setAccountId(2L);
Mockito.doReturn(quotaUsageVO).when(quotaManager).updateQuotaAllocatedVMUsage(Mockito.eq(usageVO), Mockito.any(BigDecimal.class));
assertTrue(quotaManager.aggregatePendingQuotaRecordsForAccount(accountVO, new Pair<List<? extends UsageVO>, Integer>(null, 0)).size() == 0);
assertTrue(quotaManager.aggregatePendingQuotaRecordsForAccount(accountVO, usageRecords).size() == 1);
}
@Test
public void testProcessQuotaBalanceForAccount() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
accountVO.setType(Account.ACCOUNT_TYPE_NORMAL);
QuotaUsageVO quotaUsageVO = new QuotaUsageVO();
quotaUsageVO.setAccountId(2L);
quotaUsageVO.setStartDate(new Date());
quotaUsageVO.setEndDate(new Date());
List<QuotaUsageVO> quotaListForAccount = new ArrayList<>();
quotaListForAccount.add(quotaUsageVO);
quotaManager.processQuotaBalanceForAccount(accountVO, quotaListForAccount);
Mockito.verify(quotaAcc, Mockito.times(1)).persist(Mockito.any(QuotaAccountVO.class));
}
@Test
public void testUpdateQuotaRecords() {
UsageVO usageVO = new UsageVO();
usageVO.setId(100L);
usageVO.setQuotaCalculated(0);
usageVO.setUsageType(UsageTypes.NETWORK_BYTES_SENT);
usageVO.setRawUsage(9000000000.0);
usageVO.setSize(1010101010L);
QuotaTariffVO tariffVO = new QuotaTariffVO();
tariffVO.setCurrencyValue(new BigDecimal(1));
Mockito.when(quotaTariffDao.findTariffPlanByUsageType(Mockito.anyInt(), Mockito.any(Date.class))).thenReturn(tariffVO);
quotaManager.updateQuotaNetwork(usageVO, UsageTypes.NETWORK_BYTES_SENT);
quotaManager.updateQuotaAllocatedVMUsage(usageVO, new BigDecimal(0.5));
quotaManager.updateQuotaDiskUsage(usageVO, new BigDecimal(0.5), UsageTypes.VOLUME);
quotaManager.updateQuotaRaw(usageVO, new BigDecimal(0.5), UsageTypes.VPN_USERS);
Mockito.verify(quotaUsageDao, Mockito.times(4)).persist(Mockito.any(QuotaUsageVO.class));
Mockito.verify(usageDao, Mockito.times(4)).persist(Mockito.any(UsageVO.class));
}
}