diff --git a/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java index 76af050d2ca..b32595fae2d 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java @@ -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"; diff --git a/api/src/org/apache/cloudstack/usage/UsageTypes.java b/api/src/org/apache/cloudstack/usage/UsageTypes.java index 2163df7efc5..d9cfc132e15 100644 --- a/api/src/org/apache/cloudstack/usage/UsageTypes.java +++ b/api/src/org/apache/cloudstack/usage/UsageTypes.java @@ -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 diff --git a/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java b/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java index cadf2754dbe..e5f3e27aa56 100644 --- a/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java +++ b/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java @@ -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")); + } + } diff --git a/engine/schema/src/com/cloud/usage/UsageVO.java b/engine/schema/src/com/cloud/usage/UsageVO.java index bf5f551d248..21a4196147b 100644 --- a/engine/schema/src/com/cloud/usage/UsageVO.java +++ b/engine/schema/src/com/cloud/usage/UsageVO.java @@ -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; + } } diff --git a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java index da3bb43bfe0..0d427fe17be 100644 --- a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java +++ b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java @@ -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 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 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 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 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 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 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 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 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 implements Usage @Override @SuppressWarnings("deprecation") public Pair, 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, Integer> usageRecords = new Pair, Integer>(new ArrayList(), 0); + try { + TransactionLegacy.open(TransactionLegacy.USAGE_DB).close(); + Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, 10000L); + SearchCriteria 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 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, Integer> usageRecords = searchAndCountAllRecords(sc, usageFilter); - TransactionLegacy.open(opendb).close(); return new Pair, Integer>(usageRecords.first(), usageRecords.second()); } } diff --git a/framework/quota/pom.xml b/framework/quota/pom.xml index 0ed8f8bd5a6..77d1f040b1a 100644 --- a/framework/quota/pom.xml +++ b/framework/quota/pom.xml @@ -37,5 +37,28 @@ cloud-engine-schema ${project.version} + + junit + junit + ${cs.junit.version} + test + + + org.mockito + mockito-all + ${cs.mockito.version} + test + + + org.powermock + powermock-module-junit4 + ${cs.powermock.version} + + + org.powermock + powermock-api-mockito + ${cs.powermock.version} + test + diff --git a/framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java b/framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java index c60436dc854..2b95f8b1a18 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java +++ b/framework/quota/src/org/apache/cloudstack/quota/constant/QuotaTypes.java @@ -90,6 +90,10 @@ public class QuotaTypes extends UsageTypes { } static public String getDescription(int quotaType) { - return listQuotaTypes().get(quotaType).getDescription(); + HashMap quotaMap = listQuotaTypes(); + if (quotaMap.containsKey(quotaType)) { + return quotaMap.get(quotaType).getDescription(); + } + return null; } } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java index e2c5e5acfdd..a45c98a8df2 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java @@ -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 implements QuotaAccountDao { + public static final Logger s_logger = Logger.getLogger(QuotaAccountDaoImpl.class.getName()); @Override public List listAll() { List 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 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 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 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; } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java index a0cd1acf24c..f7fd39acaf7 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaBalanceDaoImpl.java @@ -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 im @SuppressWarnings("deprecation") @Override public QuotaBalanceVO findLastBalanceEntry(final Long accountId, final Long domainId, final Date beforeThis) { + List 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 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 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 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 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 quotab = this.search(sc, filter); - TransactionLegacy.open(opendb).close(); - return quotab.size() > 0 ? quotab.get(0) : null; + List quotaBalanceEntries = new ArrayList<>(); + try { + TransactionLegacy.open(TransactionLegacy.USAGE_DB).close(); + Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, 1L); + SearchCriteria 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 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 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 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(); + List quotaBalances = new ArrayList<>(); + try { + TransactionLegacy.open(TransactionLegacy.USAGE_DB).close(); + Filter filter = new Filter(QuotaBalanceVO.class, "updatedOn", true, 0L, Long.MAX_VALUE); + SearchCriteria 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(); + } + 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 qb = search(sc, filter); - TransactionLegacy.open(opendb).close(); - return qb; + return quotaBalances; } @SuppressWarnings("deprecation") @Override public List 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 quotaUsageRecords = null; - try { + try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) { SearchCriteria sc = createSearchCriteria(); if (accountId != null) { sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); @@ -130,11 +147,12 @@ public class QuotaBalanceDaoImpl extends GenericDaoBase 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 im @Override public List lastQuotaBalanceVO(final Long accountId, final Long domainId, final Date pivotDate) { final short opendb = TransactionLegacy.currentTxn().getDatabaseId(); - - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); List quotaUsageRecords = null; List trimmedRecords = new ArrayList(); - 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 im quotaUsageRecords = search(sc, filter); // get records before startDate to find start balance - for (Iterator 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 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 im new InvalidParameterValueException("There are no balance entries on or before the requested date."); } BigDecimal finalBalance = new BigDecimal(0); - for (Iterator 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; } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java index 05e48b0f748..5d903864453 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaCreditsDaoImpl.java @@ -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 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 im @Override public List 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 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(); + List qc = new ArrayList<>(); + try { + TransactionLegacy.open(TransactionLegacy.USAGE_DB); + Filter filter = new Filter(QuotaCreditsVO.class, "updatedOn", true, 0L, Long.MAX_VALUE); + SearchCriteria 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(); + } + 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 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; } - } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java index c7b4908a181..c94a5ef34ec 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java @@ -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 implements QuotaEmailTemplatesDao { + private static final Logger s_logger = Logger.getLogger(QuotaEmailTemplatesDaoImpl.class.getName()); protected SearchBuilder QuotaEmailTemplateSearch; @@ -43,22 +47,36 @@ public class QuotaEmailTemplatesDaoImpl extends GenericDaoBase listAllQuotaEmailTemplates(String templateName) { final short opendb = TransactionLegacy.currentTxn().getDatabaseId(); - TransactionLegacy.open(TransactionLegacy.USAGE_DB); - SearchCriteria sc = QuotaEmailTemplateSearch.create(); - if (templateName != null) { - sc.setParameters("template_name", templateName); + List result = new ArrayList<>(); + try { + TransactionLegacy.open(TransactionLegacy.USAGE_DB); + SearchCriteria 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 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; } } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java index 0b3b9f184e7..71c18ef4dd5 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java @@ -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 impl @Override public QuotaTariffVO findTariffPlanByUsageType(final int quotaType, final Date effectiveDate) { final short opendb = TransactionLegacy.currentTxn().getDatabaseId(); - List result = null; - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); - try { + List result = new ArrayList<>(); + try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) { final Filter filter = new Filter(QuotaTariffVO.class, "effectiveOn", false, 0L, 1L); final SearchCriteria 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 impl public List listAllTariffPlans(final Date effectiveDate) { final short opendb = TransactionLegacy.currentTxn().getDatabaseId(); List tariffs = new ArrayList(); - 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 sc = listAllIncludedUsageType.create(); sc.setParameters("onorbefore", effectiveDate); @@ -96,47 +95,43 @@ public class QuotaTariffDaoImpl extends GenericDaoBase impl List 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; } } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDao.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDao.java index 79c55cea23e..331fb73d1d8 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDao.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDao.java @@ -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 { - List findQuotaUsage(Long accountId, Long domainId, Integer usageType, Date startDate, Date endDate); - - Pair, Integer> searchAndCountAllRecords(SearchCriteria sc, Filter filter); - - void saveQuotaUsage(List credits); - BigDecimal findTotalQuotaUsage(Long accountId, Long domainId, Integer usageType, Date startDate, Date endDate); - } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDaoImpl.java index 4604aa19eaa..a7615fd0f31 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/QuotaUsageDaoImpl.java @@ -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 implements QuotaUsageDao { - - @Override - public Pair, Integer> searchAndCountAllRecords(SearchCriteria sc, Filter filter) { - return listAndCountIncludingRemovedBy(sc, filter); - } - - @Override - public void saveQuotaUsage(List 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 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 implem @Override public List 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 quotaUsageRecords = null; - try { - // TODO instead of max value query with reasonable number and - // iterate + List quotaUsageRecords = new ArrayList(); + try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) { + // TODO instead of max value query with reasonable number and iterate SearchCriteria sc = createSearchCriteria(); if (accountId != null) { sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); @@ -84,11 +69,12 @@ public class QuotaUsageDaoImpl extends GenericDaoBase implem return new ArrayList(); } 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; } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDao.java b/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDao.java index 5d340e384f1..8353977aa0d 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDao.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDao.java @@ -21,7 +21,5 @@ import org.apache.cloudstack.quota.vo.ServiceOfferingVO; import com.cloud.utils.db.GenericDao; public interface ServiceOfferingDao extends GenericDao { - ServiceOfferingVO findServiceOffering(Long vmId, long serviceOfferingId); - } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java index 763e90518da..20e64a3c587 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/ServiceOfferingDaoImpl.java @@ -43,18 +43,19 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase customParameters) { + private ServiceOfferingVO getcomputeOffering(ServiceOfferingVO serviceOffering, Map customParameters) { ServiceOfferingVO dummyoffering = new ServiceOfferingVO(serviceOffering); dummyoffering.setDynamicFlag(true); if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) { diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDao.java b/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDao.java index f14ecf48dfd..3e226891860 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDao.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDao.java @@ -24,8 +24,5 @@ import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.quota.vo.UserVmDetailVO; public interface UserVmDetailsDao extends GenericDao { - Map listDetailsKeyPairs(long resourceId); - - } diff --git a/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDaoImpl.java b/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDaoImpl.java index 7cc42e72435..db8d5ff6939 100644 --- a/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDaoImpl.java +++ b/framework/quota/src/org/apache/cloudstack/quota/dao/UserVmDetailsDaoImpl.java @@ -48,11 +48,11 @@ public class UserVmDetailsDaoImpl extends GenericDaoBase i @Override public Map listDetailsKeyPairs(long resourceId) { + Map details = new HashMap(); SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); List results = search(sc, null); - Map details = new HashMap(results.size()); for (UserVmDetailVO result : results) { details.put(result.getName(), result.getValue()); } diff --git a/framework/quota/test/org/apache/cloudstack/quota/constant/QuotaTypesTest.java b/framework/quota/test/org/apache/cloudstack/quota/constant/QuotaTypesTest.java new file mode 100644 index 00000000000..7f608eb13e0 --- /dev/null +++ b/framework/quota/test/org/apache/cloudstack/quota/constant/QuotaTypesTest.java @@ -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 quotaTypes = QuotaTypes.listQuotaTypes(); + List 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)); + } +} \ No newline at end of file diff --git a/plugins/database/quota/pom.xml b/plugins/database/quota/pom.xml index 0330dc8a71d..7f6f739129c 100644 --- a/plugins/database/quota/pom.xml +++ b/plugins/database/quota/pom.xml @@ -1,102 +1,99 @@ + 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. --> - 4.0.0 - cloud-plugin-database-quota - Apache CloudStack Plugin - Quota Service - - org.apache.cloudstack - cloudstack-plugins - 4.6.0-SNAPSHOT - ../../pom.xml - - - - org.apache.cloudstack - cloud-api - ${project.version} - - - org.apache.cloudstack - cloud-engine-schema - ${project.version} - - - org.apache.cloudstack - cloud-utils - ${project.version} - - - org.apache.cloudstack - cloud-framework-quota - ${project.version} - - - mysql - mysql-connector-java - provided - - - org.apache.commons - commons-lang3 - ${cs.commons-lang3.version} - - - joda-time - joda-time - ${cs.joda-time.version} - - - - junit - junit - ${cs.junit.version} - test - - - org.hamcrest - hamcrest-library - ${cs.hamcrest.version} - test - - - org.mockito - mockito-all - ${cs.mockito.version} - test - - - org.powermock - powermock-module-junit4 - ${cs.powermock.version} - - - org.powermock - powermock-api-mockito - ${cs.powermock.version} - test - - - org.springframework - spring-test - ${org.springframework.version} - test - - - javax.inject - javax.inject - 1 - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + cloud-plugin-database-quota + Apache CloudStack Plugin - Quota Service + + org.apache.cloudstack + cloudstack-plugins + 4.6.0-SNAPSHOT + ../../pom.xml + + + + org.apache.cloudstack + cloud-api + ${project.version} + + + org.apache.cloudstack + cloud-engine-schema + ${project.version} + + + org.apache.cloudstack + cloud-utils + ${project.version} + + + org.apache.cloudstack + cloud-framework-quota + ${project.version} + + + mysql + mysql-connector-java + provided + + + org.apache.commons + commons-lang3 + ${cs.commons-lang3.version} + + + joda-time + joda-time + ${cs.joda-time.version} + + + junit + junit + ${cs.junit.version} + test + + + org.hamcrest + hamcrest-library + ${cs.hamcrest.version} + test + + + org.mockito + mockito-all + ${cs.mockito.version} + test + + + org.powermock + powermock-module-junit4 + ${cs.powermock.version} + + + org.powermock + powermock-api-mockito + ${cs.powermock.version} + test + + + org.springframework + spring-test + ${org.springframework.version} + test + + + javax.inject + javax.inject + 1 + + diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java index f2a75321f9c..103f5905b48 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaCreditsCmd.java @@ -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); diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmd.java index e2985febe21..da47f47e594 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmd.java @@ -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 response = new ListResponse(); diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmd.java b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmd.java index b34bd29d3af..74517f66d9c 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmd.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmd.java @@ -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; + } } diff --git a/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java b/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java index 3da648f1ec8..415a93609d3 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java +++ b/plugins/database/quota/src/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java @@ -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 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 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()); diff --git a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java index 3704098c0f7..f20bbcac33c 100644 --- a/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java +++ b/plugins/database/quota/src/org/apache/cloudstack/quota/QuotaServiceImpl.java @@ -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 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 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 accounts = _accountDao.listAccounts(accountName, domainId, filter); diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaBalanceCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaBalanceCmdTest.java new file mode 100644 index 00000000000..4369a8c9f0c --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaBalanceCmdTest.java @@ -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 quotaBalanceVOList = new ArrayList(); + 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)); + } +} \ No newline at end of file diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaCreditsCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaCreditsCmdTest.java new file mode 100644 index 00000000000..1dbb88305a7 --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaCreditsCmdTest.java @@ -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()); + + + } + +} diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmdTest.java new file mode 100644 index 00000000000..aecee6ba84c --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaEmailTemplateListCmdTest.java @@ -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 responses = new ArrayList(); + Mockito.when(responseBuilder.listQuotaEmailTemplates(Mockito.eq(cmd))).thenReturn(responses); + cmd.execute(); + Mockito.verify(responseBuilder, Mockito.times(1)).listQuotaEmailTemplates(cmd); + } +} \ No newline at end of file diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmdTest.java new file mode 100644 index 00000000000..a357a181c38 --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaEmailTemplateUpdateCmdTest.java @@ -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)); + } +} diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java new file mode 100644 index 00000000000..0492ae84b8d --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaStatementCmdTest.java @@ -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 quotaUsageVOList = new ArrayList(); + 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)); + } +} diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java new file mode 100644 index 00000000000..5781103f027 --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java @@ -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 quotaTariffVOList = new ArrayList(); + 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)); + } +} diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java new file mode 100644 index 00000000000..dc50af1c28d --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java @@ -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)); + } +} diff --git a/plugins/database/quota/test/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java b/plugins/database/quota/test/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java new file mode 100644 index 00000000000..65aadd80dee --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java @@ -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 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 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 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())); + } + + +} diff --git a/plugins/database/quota/test/org/apache/cloudstack/quota/QuotaServiceImplTest.java b/plugins/database/quota/test/org/apache/cloudstack/quota/QuotaServiceImplTest.java new file mode 100644 index 00000000000..2cd16660c4d --- /dev/null +++ b/plugins/database/quota/test/org/apache/cloudstack/quota/QuotaServiceImplTest.java @@ -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 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)); + } +} diff --git a/server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java b/server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java index 12051a6f743..7ac982db623 100644 --- a/server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java +++ b/server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java @@ -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); } } diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java index 5d1da242ae3..cd67af315da 100644 --- a/usage/src/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/com/cloud/usage/UsageManagerImpl.java @@ -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()) { diff --git a/usage/src/org/apache/cloudstack/quota/QuotaAlertManager.java b/usage/src/org/apache/cloudstack/quota/QuotaAlertManager.java index c07e01520a6..ecfe8e8c255 100644 --- a/usage/src/org/apache/cloudstack/quota/QuotaAlertManager.java +++ b/usage/src/org/apache/cloudstack/quota/QuotaAlertManager.java @@ -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); } diff --git a/usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java b/usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java index 703ee633efc..449b50abf40 100644 --- a/usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java +++ b/usage/src/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java @@ -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; - } - } diff --git a/usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java b/usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java index 568a50dc67f..5d52555579e 100644 --- a/usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java +++ b/usage/src/org/apache/cloudstack/quota/QuotaManagerImpl.java @@ -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 aggregatePendingQuotaRecordsForAccount(final AccountVO account, final Pair, Integer> usageRecords) { + List 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 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 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 accounts = _accountDao.listAll(); - for (AccountVO account : accounts) { // START ACCOUNT - Pair, Integer> usageRecords = null; - List quotalistforaccount = new ArrayList(); - 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, Integer> usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId()); + List 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 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 updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) { + public List updateQuotaRunningVMUsage(UsageVO usageRecord, final BigDecimal aggregationRatio) { List quotalist = new ArrayList(); 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; - } } diff --git a/usage/test/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java b/usage/test/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java new file mode 100644 index 00000000000..d197bc12f78 --- /dev/null +++ b/usage/test/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java @@ -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 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 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 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 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())); + } +} diff --git a/usage/test/org/apache/cloudstack/quota/QuotaManagerImplTest.java b/usage/test/org/apache/cloudstack/quota/QuotaManagerImplTest.java new file mode 100644 index 00000000000..164ff41a7ed --- /dev/null +++ b/usage/test/org/apache/cloudstack/quota/QuotaManagerImplTest.java @@ -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()); + Map 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 accountVOList = new ArrayList<>(); + accountVOList.add(accountVO); + Mockito.when(accountDao.listAll()).thenReturn(accountVOList); + + UsageVO usageVO = new UsageVO(); + usageVO.setQuotaCalculated(0); + List usageVOList = new ArrayList(); + usageVOList.add(usageVO); + Pair, Integer> usageRecords = new Pair, Integer>(usageVOList, usageVOList.size()); + Mockito.when(usageDao.getUsageRecordsPendingQuotaAggregation(Mockito.anyLong(), Mockito.anyLong())).thenReturn(usageRecords); + + QuotaUsageVO quotaUsageVO = new QuotaUsageVO(); + quotaUsageVO.setAccountId(2L); + List 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 usageVOList = new ArrayList(); + usageVOList.add(usageVO); + Pair, Integer> usageRecords = new Pair, 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, 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 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)); + } +}