diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 103ead7842f..21088592d8e 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -23,6 +23,7 @@ public class ApiConstants {
public static final String ACCOUNT_ID = "accountid";
public static final String ACCOUNT_IDS = "accountids";
public static final String ACCUMULATE = "accumulate";
+ public static final String ACTIVATION_RULE = "activationrule";
public static final String ACTIVITY = "activity";
public static final String ADAPTER_TYPE = "adaptertype";
public static final String ADDRESS = "address";
diff --git a/api/src/main/java/org/apache/cloudstack/usage/UsageUnitTypes.java b/api/src/main/java/org/apache/cloudstack/usage/UsageUnitTypes.java
new file mode 100644
index 00000000000..7ae22799f1a
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/usage/UsageUnitTypes.java
@@ -0,0 +1,52 @@
+// 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.usage;
+
+public enum UsageUnitTypes {
+ COMPUTE_MONTH ("Compute*Month"),
+ IP_MONTH ("IP*Month"),
+ GB ("GB"),
+ GB_MONTH ("GB*Month"),
+ POLICY_MONTH ("Policy*Month");
+
+ private final String description;
+
+ private UsageUnitTypes(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+
+ /**
+ * Retrieves the UsageUnitTypes according to the parameter.
+ * If there are no UsageUnitTypes with the description, it will try to retrieve it with {@link UsageUnitTypes#valueOf(String)} and will throw an
+ * {@link IllegalArgumentException} if it not exist.
+ */
+ public static UsageUnitTypes getByDescription(String description) {
+ for (UsageUnitTypes type : UsageUnitTypes.values()) {
+ if (type.toString().equals(description)) {
+ return type;
+ }
+ }
+
+ return UsageUnitTypes.valueOf(description);
+ }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/usage/UsageUnitTypesTest.java b/api/src/test/java/org/apache/cloudstack/usage/UsageUnitTypesTest.java
new file mode 100644
index 00000000000..56a30e3d135
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/usage/UsageUnitTypesTest.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.usage;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class UsageUnitTypesTest {
+
+ private List usageUnitTypes = Arrays.asList(UsageUnitTypes.values());
+
+ @Test
+ public void getByDescriptionTestAllTheDescriptionsMustReturnUsageUnitTypes() {
+ usageUnitTypes.forEach(type -> {
+ UsageUnitTypes usageUnitType = UsageUnitTypes.getByDescription(type.toString());
+ Assert.assertEquals(type, usageUnitType);
+ });
+ }
+
+ @Test
+ public void getByDescriptionTestAllTheConstantNamesMustReturnUsageUnitTypes() {
+ usageUnitTypes.forEach(type -> {
+ UsageUnitTypes usageUnitType = UsageUnitTypes.getByDescription(type.name());
+ Assert.assertEquals(type, usageUnitType);
+ });
+ }
+
+ @Test (expected = IllegalArgumentException.class)
+ public void getByDescriptionTestPassWrongTypeOrDescriptionAndThrowsIllegalArgumentException() {
+ UsageUnitTypes.getByDescription("test");
+ }
+}
diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java
index acd26899716..ecc412aa79d 100644
--- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java
+++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java
@@ -18,10 +18,16 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
import java.util.List;
+import org.apache.cloudstack.framework.config.ConfigKey;
+
import com.cloud.storage.Snapshot;
import com.cloud.utils.exception.CloudRuntimeException;
public interface SnapshotInfo extends DataObject, Snapshot {
+ ConfigKey BackupSnapshotAfterTakingSnapshot = new ConfigKey<>(Boolean.class, "snapshot.backup.to.secondary", "Snapshots", "true", "Indicates whether to always"
+ + " backup primary storage snapshot to secondary storage. Keeping snapshots only on Primary storage is applicable for KVM + Ceph only.", false, ConfigKey.Scope.Global,
+ null);
+
SnapshotInfo getParent();
String getPath();
diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index e42d98a8bce..3bad014dbf9 100755
--- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -625,8 +625,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final VirtualMachineGuru guru = getVmGuru(vm);
guru.finalizeExpunge(vm);
- userVmDetailsDao.removeDetails(vm.getId());
-
userVmDeployAsIsDetailsDao.removeDetails(vm.getId());
// Remove comments (if any)
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 18cd38180bc..07b475af67a 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -126,7 +126,6 @@ import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VMTemplateDetailsDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
-import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.template.TemplateManager;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
@@ -243,7 +242,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
private final StateMachine2 _volStateMachine;
protected List _storagePoolAllocators;
- protected boolean backupSnapshotAfterTakingSnapshot = SnapshotManager.BackupSnapshotAfterTakingSnapshot.value();
+ protected boolean backupSnapshotAfterTakingSnapshot = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
public List getStoragePoolAllocators() {
return _storagePoolAllocators;
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java
index 6b41ba42b22..98497512fce 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41610to41700.java
@@ -18,6 +18,7 @@ package com.cloud.upgrade.dao;
import com.cloud.upgrade.SystemVmTemplateRegistration;
import com.cloud.utils.exception.CloudRuntimeException;
+
import org.apache.log4j.Logger;
import java.io.InputStream;
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41800.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41800.java
new file mode 100644
index 00000000000..6241901dc4f
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41800.java
@@ -0,0 +1,233 @@
+// 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 com.cloud.upgrade.dao;
+
+import com.cloud.upgrade.SystemVmTemplateRegistration;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.api.response.UsageTypeResponse;
+import org.apache.cloudstack.usage.UsageTypes;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.log4j.Logger;
+
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Upgrade41700to41800 implements DbUpgrade, DbUpgradeSystemVmTemplate {
+
+ final static Logger LOG = Logger.getLogger(Upgrade41700to41800.class);
+ private SystemVmTemplateRegistration systemVmTemplateRegistration;
+
+ @Override
+ public String[] getUpgradableVersionRange() {
+ return new String[] {"4.17.0.0", "4.18.0.0"};
+ }
+
+ @Override
+ public String getUpgradedVersion() {
+ return "4.18.0.0";
+ }
+
+ @Override
+ public boolean supportsRollingUpgrade() {
+ return false;
+ }
+
+ @Override
+ public InputStream[] getPrepareScripts() {
+ final String scriptFile = "META-INF/db/schema-41700to41800.sql";
+ final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
+ if (script == null) {
+ throw new CloudRuntimeException("Unable to find " + scriptFile);
+ }
+
+ return new InputStream[] {script};
+ }
+
+ @Override
+ public void performDataMigration(Connection conn) {
+ convertQuotaTariffsToNewParadigm(conn);
+ convertVmResourcesQuotaTypesToRunningVmQuotaType(conn);
+ }
+
+ @Override
+ public InputStream[] getCleanupScripts() {
+ final String scriptFile = "META-INF/db/schema-41700to41800-cleanup.sql";
+ final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
+ if (script == null) {
+ throw new CloudRuntimeException("Unable to find " + scriptFile);
+ }
+
+ return new InputStream[] {script};
+ }
+
+ private void initSystemVmTemplateRegistration() {
+ systemVmTemplateRegistration = new SystemVmTemplateRegistration();
+ }
+
+ @Override
+ public void updateSystemVmTemplates(Connection conn) {
+ LOG.debug("Updating System Vm template IDs");
+ initSystemVmTemplateRegistration();
+ try {
+ systemVmTemplateRegistration.updateSystemVmTemplates(conn);
+ } catch (Exception e) {
+ throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
+ }
+ }
+
+ protected void convertQuotaTariffsToNewParadigm(Connection conn) {
+ LOG.info("Converting quota tariffs to new paradigm.");
+
+ List usageTypeResponses = UsageTypes.listUsageTypes();
+
+ for (UsageTypeResponse usageTypeResponse : usageTypeResponses) {
+ Integer usageType = usageTypeResponse.getUsageType();
+
+ String tariffTypeDescription = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(usageTypeResponse, "description", "usageType");
+
+ LOG.info(String.format("Converting quota tariffs of type %s to new paradigm.", tariffTypeDescription));
+
+ for (boolean previousTariff : Arrays.asList(true, false)) {
+ Map tariffs = selectTariffs(conn, usageType, previousTariff, tariffTypeDescription);
+
+ int tariffsSize = tariffs.size();
+ if (tariffsSize < 2) {
+ LOG.info(String.format("Quota tariff of type %s has [%s] %s register(s). Tariffs with less than 2 register do not need to be converted to new paradigm.",
+ tariffTypeDescription, tariffsSize, previousTariff ? "previous of current" : "next to current"));
+ continue;
+ }
+
+ executeUpdateQuotaTariffSetEndDateAndRemoved(conn, usageType, tariffs, previousTariff, tariffTypeDescription);
+ }
+ }
+ }
+
+ protected Map selectTariffs(Connection conn, Integer usageType, boolean previousTariff, String tariffTypeDescription) {
+ Map quotaTariffs = new LinkedHashMap<>();
+
+ String selectQuotaTariffs = String.format("SELECT id, effective_on FROM cloud_usage.quota_tariff WHERE %s AND usage_type = ? ORDER BY effective_on, updated_on;",
+ previousTariff ? "usage_name = name" : "removed is null");
+
+ LOG.info(String.format("Selecting %s quota tariffs of type [%s] according to SQL [%s].", previousTariff ? "previous of current" : "next to current",
+ tariffTypeDescription, selectQuotaTariffs));
+
+ try (PreparedStatement pstmt = conn.prepareStatement(selectQuotaTariffs)) {
+ pstmt.setInt(1, usageType);
+
+ try (ResultSet result = pstmt.executeQuery()) {
+ while (result.next()) {
+ quotaTariffs.put(result.getLong("id"), result.getDate("effective_on"));
+ }
+ }
+ return quotaTariffs;
+ } catch (SQLException e) {
+ String message = String.format("Unable to retrieve %s quota tariffs of type [%s] due to [%s].", previousTariff ? "previous" : "next", tariffTypeDescription,
+ e.getMessage());
+ LOG.error(message, e);
+ throw new CloudRuntimeException(message, e);
+ }
+ }
+
+ protected void executeUpdateQuotaTariffSetEndDateAndRemoved(Connection conn, Integer usageType, Map tariffs, boolean setRemoved, String tariffTypeDescription) {
+ String updateQuotaTariff = String.format("UPDATE cloud_usage.quota_tariff SET end_date = ? %s WHERE id = ?;", setRemoved ? ", removed = ?" : "");
+
+ Object[] ids = tariffs.keySet().toArray();
+
+ LOG.info(String.format("Updating %s registers of %s quota tariffs of type [%s] with SQL [%s].", tariffs.size() -1, setRemoved ? "previous of current" :
+ "next to current", tariffTypeDescription, updateQuotaTariff));
+
+ for (int i = 0; i < tariffs.size() - 1; i++) {
+ Long id = Long.valueOf(String.valueOf(ids[i]));
+ Long nextId = Long.valueOf(String.valueOf(ids[i + 1]));
+
+ Date endDate = tariffs.get(nextId);
+
+ if (!DateUtils.isSameDay(endDate, tariffs.get(id))) {
+ endDate = DateUtils.addDays(endDate, -1);
+ }
+
+ try (PreparedStatement pstmt = conn.prepareStatement(updateQuotaTariff)) {
+ java.sql.Date sqlEndDate = new java.sql.Date(endDate.getTime());
+ pstmt.setDate(1, sqlEndDate);
+
+ String updateRemoved = "";
+ if (setRemoved) {
+ pstmt.setDate(2, sqlEndDate);
+ pstmt.setLong(3, id);
+
+ updateRemoved = String.format("and \"removed\" to [%s] ", sqlEndDate);
+ } else {
+ pstmt.setLong(2, id);
+ }
+
+ LOG.info(String.format("Updating \"end_date\" to [%s] %sof quota tariff with ID [%s].", sqlEndDate, updateRemoved, id));
+ pstmt.executeUpdate();
+ } catch (SQLException e) {
+ String message = String.format("Unable to update \"end_date\" %s of quota tariffs of usage type [%s] due to [%s].", setRemoved ? "and \"removed\"" : "",
+ usageType, e.getMessage());
+ LOG.error(message, e);
+ throw new CloudRuntimeException(message, e);
+ }
+ }
+ }
+
+ protected void convertVmResourcesQuotaTypesToRunningVmQuotaType(Connection conn) {
+ LOG.info("Converting quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\" to \"RUNNING_VM\".");
+
+ String insertSql = String.format("INSERT INTO cloud_usage.quota_tariff (usage_type, usage_name, usage_unit, usage_discriminator, currency_value, effective_on, updated_on,"
+ + " updated_by, uuid, name, description, removed, end_date, activation_rule)\n"
+ + "SELECT 1, 'RUNNING_VM', usage_unit, '', 0, effective_on, updated_on, updated_by, UUID(), name, description, removed, end_date,\n"
+ + " CASE\n"
+ + " WHEN usage_type = 15 THEN CONCAT('((value.computingResources ? (value.computingResources.cpuSpeed * value.computingResources.cpuNumber) : 0) / 100) * ', currency_value)\n"
+ + " WHEN usage_type = 16 THEN CONCAT('(value.computingResources ? value.computingResources.cpuNumber : 0) * ', currency_value)\n"
+ + " WHEN usage_type = 17 THEN CONCAT('(value.computingResources ? value.computingResources.memory : 0)* ', currency_value)\n"
+ + " END\n"
+ + "FROM cloud_usage.quota_tariff \n"
+ + "WHERE usage_type in (15, 16, 17) \n"
+ + "AND currency_value > 0.0;");
+
+ try (PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
+ pstmt.executeUpdate();
+ } catch (SQLException e) {
+ String message = String.format("Failed to convert quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\" to \"RUNNING_VM\" due to [%s].", e.getMessage());
+ LOG.error(message, e);
+ throw new CloudRuntimeException(message, e);
+ }
+
+ LOG.info("Disabling unused quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\".");
+
+ String updateSql = "UPDATE cloud_usage.quota_tariff SET removed = now() WHERE usage_type in (15, 16, 17) and removed is null;";
+
+ try (PreparedStatement pstmt = conn.prepareStatement(updateSql)) {
+ pstmt.executeUpdate();
+ } catch (SQLException e) {
+ String message = String.format("Failed disable quota tariffs of type \"vCPU\", \"CPU_SPEED\" and \"MEMORY\" due to [%s].", e.getMessage());
+ LOG.error(message, e);
+ throw new CloudRuntimeException(message, e);
+ }
+ }
+}
diff --git a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java
index 45ceeeac9a6..10b295f593c 100644
--- a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java
+++ b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java
@@ -29,6 +29,7 @@ import javax.persistence.TemporalType;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.usage.Usage;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
@Entity
@Table(name = "cloud_usage")
@@ -396,4 +397,9 @@ public class UsageVO implements Usage, InternalIdentity {
public void setHidden(boolean hidden) {
this.isHidden = hidden;
}
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "usageId", "usageType", "startDate", "endDate");
+ }
}
diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java
index 4822dd60acf..4099b3ada0d 100644
--- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java
+++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDao.java
@@ -25,6 +25,7 @@ import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.SearchCriteria;
+import java.util.Date;
import java.util.List;
public interface UsageDao extends GenericDao {
@@ -58,5 +59,7 @@ public interface UsageDao extends GenericDao {
UsageVO persistUsage(final UsageVO usage);
- Pair, Integer> getUsageRecordsPendingQuotaAggregation(long accountId, long domainId);
+ Pair, Integer> listUsageRecordsPendingForQuotaAggregation(long accountId, long domainId);
+
+ List> listAccountResourcesInThePeriod(long accountId, int usageType, Date startDate, Date endDate);
}
diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java
index 76362e6d730..4553ed822b4 100644
--- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java
@@ -38,6 +38,8 @@ import org.springframework.stereotype.Component;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Date;
@@ -73,6 +75,13 @@ public class UsageDaoImpl extends GenericDaoBase implements Usage
protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT");
+ private static final String LIST_ACCOUNT_RESOURCES_IN_PERIOD = "SELECT zone.uuid as zone_uuid, domain.uuid as domain_uuid\n "
+ + "FROM cloud_usage.cloud_usage cloud_usage\n "
+ + "INNER JOIN cloud.data_center zone ON (zone.id = cloud_usage.zone_id)\n "
+ + "INNER JOIN cloud.domain domain ON (domain.id = cloud_usage.domain_id)\n "
+ + "WHERE cloud_usage.usage_type = ? AND cloud_usage.account_id = ? AND cloud_usage.start_date >= ? AND cloud_usage.end_date <= ? "
+ + "GROUP BY cloud_usage.usage_id ";
+
public UsageDaoImpl() {
}
@@ -486,30 +495,61 @@ public class UsageDaoImpl extends GenericDaoBase implements Usage
});
}
- 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);
- }
- return Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback, Integer>>() {
- @Override
- public Pair, Integer> doInTransaction(final TransactionStatus status) {
- Pair, Integer> usageRecords = new Pair, Integer>(new ArrayList(), 0);
- Filter usageFilter = new Filter(UsageVO.class, "startDate", true, 0L, Long.MAX_VALUE);
- QueryBuilder qb = QueryBuilder.create(UsageVO.class);
- if (accountId != -1) {
- qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
- }
- if (domainId != -1) {
- qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
- }
- qb.and(qb.entity().getQuotaCalculated(), SearchCriteria.Op.NEQ, 1);
- qb.and(qb.entity().getRawUsage(), SearchCriteria.Op.GT, 0);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Getting usage records" + usageFilter.getOrderBy());
- }
- usageRecords = searchAndCountAllRecords(qb.create(), usageFilter);
- return new Pair, Integer>(usageRecords.first(), usageRecords.second());
+ @Override
+ public Pair, Integer> listUsageRecordsPendingForQuotaAggregation(long accountId, long domainId) {
+ s_logger.debug(String.format("Retrieving pending usage records for accountId [%s] and domainId [%s].", accountId, domainId));
+
+ return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback, Integer>>) status -> {
+ Filter usageFilter = new Filter(UsageVO.class, "startDate", true, null, null);
+ QueryBuilder qb = QueryBuilder.create(UsageVO.class);
+
+ if (accountId != -1) {
+ qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
}
+
+ if (domainId != -1) {
+ qb.and(qb.entity().getDomainId(), SearchCriteria.Op.EQ, domainId);
+ }
+
+ qb.and(qb.entity().getQuotaCalculated(), SearchCriteria.Op.NEQ, 1);
+ qb.and(qb.entity().getRawUsage(), SearchCriteria.Op.GT, 0);
+
+ return searchAndCountAllRecords(qb.create(), usageFilter);
});
}
+
+ @Override
+ public List> listAccountResourcesInThePeriod(long accountId, int usageType, Date startDate, Date endDate) {
+ String startDateString = DateUtil.getOutputString(startDate);
+ String endDateString = DateUtil.getOutputString(endDate);
+
+ s_logger.debug(String.format("Retrieving account resources between [%s] and [%s] for accountId [%s] and usageType [%s].", startDateString, endDateString, accountId,
+ usageType));
+
+ TransactionLegacy txn = TransactionLegacy.currentTxn();
+ try (PreparedStatement pstmt = txn.prepareStatement(LIST_ACCOUNT_RESOURCES_IN_PERIOD)) {
+ List> accountResourcesOfTheLastDay = new ArrayList<>();
+
+ pstmt.setInt(1, usageType);
+ pstmt.setLong(2, accountId);
+ pstmt.setTimestamp(3, new Timestamp(startDate.getTime()));
+ pstmt.setTimestamp(4, new Timestamp(endDate.getTime()));
+
+ try (ResultSet rs = pstmt.executeQuery()) {
+ while (rs.next()) {
+ String zoneUuid = rs.getString("zone_uuid");
+ String domainUuid = rs.getString("domain_uuid");
+
+ accountResourcesOfTheLastDay.add(new Pair<>(zoneUuid, domainUuid));
+ }
+ }
+
+ return accountResourcesOfTheLastDay;
+ } catch (SQLException e) {
+ s_logger.error(String.format("Failed to retrieve account resources between [%s] and [%s] for accountId [%s] and usageType [%s] due to [%s]. Returning an empty list of"
+ + " resources.", startDateString, endDateString, accountId, usageType, e.getMessage()), e);
+
+ return new ArrayList<>();
+ }
+ }
}
diff --git a/engine/schema/src/main/java/com/cloud/user/AccountVO.java b/engine/schema/src/main/java/com/cloud/user/AccountVO.java
index 50793fbd208..f04b2bafbde 100644
--- a/engine/schema/src/main/java/com/cloud/user/AccountVO.java
+++ b/engine/schema/src/main/java/com/cloud/user/AccountVO.java
@@ -225,4 +225,8 @@ public class AccountVO implements Account {
public String getName() {
return accountName;
}
+
+ public String reflectionToString() {
+ return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "accountName", "domainId");
+ }
}
diff --git a/engine/schema/src/main/java/com/cloud/vm/constants/VmDetails.java b/engine/schema/src/main/java/com/cloud/vm/constants/VmDetails.java
new file mode 100644
index 00000000000..fa3aeab087d
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/vm/constants/VmDetails.java
@@ -0,0 +1,32 @@
+// 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 com.cloud.vm.constants;
+
+public enum VmDetails {
+ MEMORY("memory"), CPU_NUMBER("cpuNumber"), CPU_SPEED("cpuSpeed");
+
+ private String name;
+
+ private VmDetails(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java
similarity index 99%
rename from engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java
rename to engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java
index c3cfe89d491..066a36ddff4 100644
--- a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java
@@ -14,7 +14,7 @@
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
-package org.apache.cloudstack.storage.image.db;
+package org.apache.cloudstack.storage.datastore.db;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -30,8 +30,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml
new file mode 100644
index 00000000000..eba4036e0aa
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index b670621de9f..b35054ecd8a 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -27,26 +27,22 @@
http://www.springframework.org/schema/context/spring-context.xsd"
>
+
+
-
-
-
-
-
-
@@ -59,18 +55,12 @@
-
-
-
-
-
-
@@ -95,24 +85,17 @@
-
-
-
-
-
-
-
@@ -139,13 +122,10 @@
-
-
-
@@ -157,14 +137,11 @@
-
-
-
@@ -180,7 +157,6 @@
-
@@ -195,7 +171,6 @@
-
@@ -205,9 +180,7 @@
-
-
@@ -235,15 +208,12 @@
-
-
-
@@ -251,7 +221,6 @@
-
@@ -263,13 +232,9 @@
-
-
-
-
@@ -283,7 +248,6 @@
-
@@ -293,7 +257,6 @@
-
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41700to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41700to41800.sql
new file mode 100644
index 00000000000..d3e40547d91
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41700to41800.sql
@@ -0,0 +1,88 @@
+-- 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.
+
+--;
+-- Schema upgrade from 4.17.0.0 to 4.18.0.0
+--;
+
+----- PR Quota custom tariffs #5909---
+-- Create column 'uuid'
+ALTER TABLE cloud_usage.quota_tariff
+ ADD COLUMN `uuid` varchar(40);
+
+UPDATE cloud_usage.quota_tariff
+SET uuid = UUID()
+WHERE uuid is null;
+
+ALTER TABLE cloud_usage.quota_tariff
+ MODIFY `uuid` varchar(40) NOT NULL;
+
+
+-- Create column 'name'
+ALTER TABLE cloud_usage.quota_tariff
+ ADD COLUMN `name` text
+ COMMENT 'A name, defined by the user, to the tariff. This column will be used as identifier along the tariff updates.';
+
+UPDATE cloud_usage.quota_tariff
+SET name = case when effective_on <= now() then usage_name else concat(usage_name, '-', id) end
+WHERE name is null;
+
+ALTER TABLE cloud_usage.quota_tariff
+ MODIFY `name` text NOT NULL;
+
+
+-- Create column 'description'
+ALTER TABLE cloud_usage.quota_tariff
+ ADD COLUMN `description` text DEFAULT NULL
+ COMMENT 'The description of the tariff.';
+
+
+-- Create column 'activation_rule'
+ALTER TABLE cloud_usage.quota_tariff
+ ADD COLUMN `activation_rule` text DEFAULT NULL
+ COMMENT 'JS expression that defines when the tariff should be activated.';
+
+
+-- Create column 'removed'
+ALTER TABLE cloud_usage.quota_tariff
+ ADD COLUMN `removed` datetime DEFAULT NULL;
+
+
+-- Create column 'end_date'
+ALTER TABLE cloud_usage.quota_tariff
+ ADD COLUMN `end_date` datetime DEFAULT NULL
+ COMMENT 'Defines the end date of the tarrif.';
+
+
+-- Change usage unit to right unit
+UPDATE cloud_usage.quota_tariff
+SET usage_unit = 'Compute*Month'
+WHERE usage_unit = 'Compute-Month';
+
+UPDATE cloud_usage.quota_tariff
+SET usage_unit = 'IP*Month'
+WHERE usage_unit = 'IP-Month';
+
+UPDATE cloud_usage.quota_tariff
+SET usage_unit = 'GB*Month'
+WHERE usage_unit = 'GB-Month';
+
+UPDATE cloud_usage.quota_tariff
+SET usage_unit = 'Policy*Month'
+WHERE usage_unit = 'Policy-Month';
+
+----- PR Quota custom tariffs #5909 -----
diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
index 6056defcc9b..1d463cad7ea 100644
--- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
@@ -71,7 +71,6 @@ import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StoragePool;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
-import static com.cloud.storage.snapshot.SnapshotManager.BackupSnapshotAfterTakingSnapshot;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
@@ -585,7 +584,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
}
Map options = new HashMap();
options.put("fullSnapshot", fullSnapshot.toString());
- options.put(BackupSnapshotAfterTakingSnapshot.key(), String.valueOf(BackupSnapshotAfterTakingSnapshot.value()));
+ options.put(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(), String.valueOf(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value()));
boolean encryptionRequired = anyVolumeRequiresEncryption(srcData, destData);
Answer answer = null;
diff --git a/engine/storage/integration-test/src/test/resources/fakeDriverTestContext.xml b/engine/storage/integration-test/src/test/resources/fakeDriverTestContext.xml
index b8a227476ac..3c2298ee16b 100644
--- a/engine/storage/integration-test/src/test/resources/fakeDriverTestContext.xml
+++ b/engine/storage/integration-test/src/test/resources/fakeDriverTestContext.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/engine/storage/integration-test/src/test/resources/storageContext.xml b/engine/storage/integration-test/src/test/resources/storageContext.xml
index f65e2accde9..fc24753127e 100644
--- a/engine/storage/integration-test/src/test/resources/storageContext.xml
+++ b/engine/storage/integration-test/src/test/resources/storageContext.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java b/engine/storage/src/test/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImplTest.java
similarity index 98%
rename from engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java
rename to engine/storage/src/test/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImplTest.java
index 0a02721f67f..b3b9efcbf90 100644
--- a/engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java
+++ b/engine/storage/src/test/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImplTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.cloudstack.storage.image.db;
+package org.apache.cloudstack.storage.datastore.db;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.DataStoreRole;
@@ -25,7 +25,6 @@ import com.cloud.storage.SnapshotVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java
index 38f9d9f64a1..1eae0edd9c3 100644
--- a/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java
+++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java
@@ -270,6 +270,8 @@ public interface GenericDao {
*/
Pair, Integer> searchAndCount(SearchCriteria sc, Filter filter);
+ Pair, Integer> searchAndCount(SearchCriteria sc, Filter filter, boolean includeRemoved);
+
/**
* @param sc
* @param filter
diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java
index 208e8463e72..1ab2c196ff8 100644
--- a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java
+++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java
@@ -56,7 +56,6 @@ import javax.persistence.Enumerated;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
-import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.utils.DateUtil;
@@ -71,6 +70,8 @@ import com.cloud.utils.db.SearchCriteria.SelectType;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.Ip;
import com.cloud.utils.net.NetUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
@@ -1320,17 +1321,43 @@ public abstract class GenericDaoBase extends Compone
}
@Override
- @DB()
public Pair, Integer> searchAndCount(final SearchCriteria sc, final Filter filter) {
- List objects = search(sc, filter, null, false);
- Integer count = getCount(sc);
- // Count cannot be less than the result set but can be higher due to pagination, see CLOUDSTACK-10320
- if (count < objects.size()) {
- count = objects.size();
+ return searchAndCount(sc, filter, false);
+ }
+
+ @Override
+ @DB()
+ public Pair, Integer> searchAndCount(SearchCriteria sc, final Filter filter, boolean includeRemoved) {
+ if (!includeRemoved) {
+ sc = checkAndSetRemovedIsNull(sc);
}
+
+ List objects = searchIncludingRemoved(sc, filter, null, false);
+ int count = getCountIncludingRemoved(sc);
+
+ count = checkCountOfRecordsAgainstTheResultSetSize(count, objects.size());
+
return new Pair, Integer>(objects, count);
}
+ /**
+ * Validates if the count of records is higher or equal to the result set's size.
+ * Count cannot be less than the result set, however, it can be higher due to pagination (see CLOUDSTACK-10320).
+ * @return Count if it is higher or equal to the result set's size, otherwise the result set's size.
+ */
+ protected int checkCountOfRecordsAgainstTheResultSetSize(int count, int resultSetSize) {
+ if (count >= resultSetSize) {
+ return count;
+ }
+
+ String stackTrace = ExceptionUtils.getStackTrace(new CloudRuntimeException(String.format("The query to count all the records of [%s] resulted in a value smaller than"
+ + " the result set's size [count of records: %s, result set's size: %s]. Using the result set's size instead.", _entityBeanType,
+ count, resultSetSize)));
+ s_logger.warn(stackTrace);
+
+ return resultSetSize;
+ }
+
@Override
@DB()
public Pair, Integer> searchAndDistinctCount(final SearchCriteria sc, final Filter filter) {
diff --git a/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java b/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java
index 608f715018c..6f90d2391e6 100644
--- a/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java
+++ b/framework/db/src/main/java/com/cloud/utils/db/SearchCriteria.java
@@ -23,6 +23,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.commons.lang3.ArrayUtils;
+
import com.cloud.utils.Pair;
import com.cloud.utils.db.SearchBase.Condition;
import com.cloud.utils.db.SearchBase.Select;
@@ -157,6 +159,12 @@ public class SearchCriteria {
return fields;
}
+ public void setParametersIfNotNull(String conditionName, Object... params) {
+ if (ArrayUtils.isNotEmpty(params) && (params.length > 1 || params[0] != null)) {
+ setParameters(conditionName, params);
+ }
+ }
+
public void setParameters(String conditionName, Object... params) {
assert _conditions.contains(new Condition(conditionName)) || _additionals.contains(new Condition(conditionName)) : "Couldn't find " + conditionName;
_params.put(conditionName, params);
diff --git a/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java b/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java
index 26530a2f62d..352ea735aa7 100644
--- a/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java
+++ b/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java
@@ -39,6 +39,8 @@ public class GenericDaoBaseTest {
private static final String INTEGRITY_CONSTRAINT_VIOLATION = "23000";
private static final int DUPLICATE_ENTRY_ERRO_CODE = 1062;
+ GenericDaoBase genericDaoBaseMock = Mockito.mock(GenericDaoBase.class, Mockito.CALLS_REAL_METHODS);
+
@Before
public void prepareTests() throws SQLException {
Mockito.when(resultSet.getObject(1)).thenReturn(false);
@@ -182,4 +184,34 @@ public class GenericDaoBaseTest {
GenericDaoBase.handleEntityExistsException(mockedSQLException);
}
+ @Test
+ public void checkCountOfRecordsAgainstTheResultSetSizeTestCountHigherThanResultSetSize() {
+ int count = 10;
+ int resultSetSize = 5;
+
+ int result = genericDaoBaseMock.checkCountOfRecordsAgainstTheResultSetSize(count, resultSetSize);
+
+ Assert.assertEquals(count, result);
+ }
+
+ @Test
+ public void checkCountOfRecordsAgainstTheResultSetSizeTestCountEqualToResultSetSize() {
+ int count = 10;
+ int resultSetSize = 10;
+
+ int result = genericDaoBaseMock.checkCountOfRecordsAgainstTheResultSetSize(count, resultSetSize);
+
+ Assert.assertEquals(count, result);
+ Assert.assertEquals(resultSetSize, result);
+ }
+
+ @Test
+ public void checkCountOfRecordsAgainstTheResultSetSizeTestCountSmallerThanResultSetSize() {
+ int count = 5;
+ int resultSetSize = 10;
+
+ int result = genericDaoBaseMock.checkCountOfRecordsAgainstTheResultSetSize(count, resultSetSize);
+
+ Assert.assertEquals(resultSetSize, result);
+ }
}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
index 0b384010127..e74bf681cf2 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
@@ -19,28 +19,42 @@ package org.apache.cloudstack.quota;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
+import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.user.Account;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
+import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
+import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
-import org.apache.cloudstack.quota.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.usage.UsageTypes;
+import org.apache.cloudstack.usage.UsageUnitTypes;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.usage.UsageUtils;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -48,6 +62,7 @@ import com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.DateUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
@@ -66,18 +81,22 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
@Inject
private QuotaUsageDao _quotaUsageDao;
@Inject
- private ServiceOfferingDao _serviceOfferingDao;
- @Inject
private QuotaBalanceDao _quotaBalanceDao;
@Inject
private ConfigurationDao _configDao;
+ @Inject
+ protected PresetVariableHelper presetVariableHelper;
+
private TimeZone _usageTimezone;
private int _aggregationDuration = 0;
- final static BigDecimal s_hoursInMonth = new BigDecimal(30 * 24);
- final static BigDecimal s_minutesInMonth = new BigDecimal(30 * 24 * 60);
- final static BigDecimal s_gb = new BigDecimal(1024 * 1024 * 1024);
+ static final BigDecimal s_hoursInMonth = BigDecimal.valueOf(DateUtil.HOURS_IN_A_MONTH);
+ static final BigDecimal GiB_DECIMAL = BigDecimal.valueOf(ByteScaleUtils.GiB);
+ List lockablesAccountTypes = Arrays.asList(Account.Type.NORMAL, Account.Type.DOMAIN_ADMIN);
+
+ List usageTypesToAvoidCalculation = Arrays.asList(UsageTypes.VM_DISK_IO_READ, UsageTypes.VM_DISK_IO_WRITE, UsageTypes.VM_DISK_BYTES_READ,
+ UsageTypes.VM_DISK_BYTES_WRITE);
public QuotaManagerImpl() {
super();
@@ -133,342 +152,408 @@ 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()) {
- switch (usageRecord.getUsageType()) {
- case QuotaTypes.RUNNING_VM:
- List lq = updateQuotaRunningVMUsage(usageRecord);
- if (!lq.isEmpty()) {
- quotaListForAccount.addAll(lq);
- }
- break;
- case QuotaTypes.ALLOCATED_VM:
- QuotaUsageVO qu = updateQuotaAllocatedVMUsage(usageRecord);
- if (qu != null) {
- quotaListForAccount.add(qu);
- }
- break;
- case QuotaTypes.SNAPSHOT:
- case QuotaTypes.TEMPLATE:
- case QuotaTypes.ISO:
- case QuotaTypes.VOLUME:
- case QuotaTypes.VM_SNAPSHOT:
- case QuotaTypes.BACKUP:
- qu = updateQuotaDiskUsage(usageRecord, usageRecord.getUsageType());
- if (qu != null) {
- quotaListForAccount.add(qu);
- }
- 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:
- qu = updateQuotaRaw(usageRecord, usageRecord.getUsageType());
- if (qu != null) {
- quotaListForAccount.add(qu);
- }
- break;
- case QuotaTypes.NETWORK_BYTES_RECEIVED:
- case QuotaTypes.NETWORK_BYTES_SENT:
- qu = updateQuotaNetwork(usageRecord, usageRecord.getUsageType());
- if (qu != null) {
- quotaListForAccount.add(qu);
- }
- 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;
- }
+ protected void processQuotaBalanceForAccount(AccountVO accountVo, List accountQuotaUsages) {
+ String accountToString = accountVo.reflectionToString();
- public void processQuotaBalanceForAccount(final AccountVO account, final List quotaListForAccount) {
- if (quotaListForAccount == null || quotaListForAccount.isEmpty()) {
+ if (CollectionUtils.isEmpty(accountQuotaUsages)) {
+ s_logger.info(String.format("Account [%s] does not have quota usages to process. Skipping it.", accountToString));
return;
}
- if (s_logger.isDebugEnabled()) {
- s_logger.debug(quotaListForAccount.get(0));
- }
- Date startDate = quotaListForAccount.get(0).getStartDate();
- Date endDate = quotaListForAccount.get(0).getEndDate();
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("processQuotaBalanceForAccount startDate " + startDate + " endDate=" + endDate);
- s_logger.debug("processQuotaBalanceForAccount last items startDate " + quotaListForAccount.get(quotaListForAccount.size() - 1).getStartDate() + " items endDate="
- + quotaListForAccount.get(quotaListForAccount.size() - 1).getEndDate());
- }
- quotaListForAccount.add(new QuotaUsageVO());
- BigDecimal aggrUsage = new BigDecimal(0);
- List creditsReceived = null;
- //bootstrapping
- QuotaUsageVO lastQuotaUsage = _quotaUsageDao.findLastQuotaUsageEntry(account.getAccountId(), account.getDomainId(), startDate);
- if (lastQuotaUsage == null) {
- aggrUsage = aggrUsage.add(aggregateCreditBetweenDates(account, new Date(0), startDate));
- // create a balance entry for these accumulated credits
- QuotaBalanceVO firstBalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, startDate);
- _quotaBalanceDao.saveQuotaBalance(firstBalance);
- } else {
- QuotaBalanceVO lastRealBalanceEntry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), endDate);
- if (lastRealBalanceEntry != null){
- aggrUsage = aggrUsage.add(lastRealBalanceEntry.getCreditBalance());
- }
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Last balance entry " + lastRealBalanceEntry + " AggrUsage=" + aggrUsage);
- }
- // get all the credit entries after this balance and add
- aggrUsage = aggrUsage.add(aggregateCreditBetweenDates(account, lastRealBalanceEntry.getUpdatedOn(), endDate));
- }
+ QuotaUsageVO firstQuotaUsage = accountQuotaUsages.get(0);
+ Date startDate = firstQuotaUsage.getStartDate();
+ Date endDate = firstQuotaUsage.getStartDate();
- for (QuotaUsageVO entry : quotaListForAccount) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Usage entry found " + entry);
- }
- if (entry.getQuotaUsed().compareTo(BigDecimal.ZERO) == 0) {
- // check if there were credits and aggregate
- aggrUsage = aggrUsage.add(aggregateCreditBetweenDates(account, entry.getStartDate(), entry.getEndDate()));
+ s_logger.info(String.format("Processing quota balance for account [%s] between [%s] and [%s].", accountToString, startDate,
+ accountQuotaUsages.get(accountQuotaUsages.size() - 1).getEndDate()));
+
+ BigDecimal aggregatedUsage = BigDecimal.ZERO;
+ long accountId = accountVo.getAccountId();
+ long domainId = accountVo.getDomainId();
+
+ aggregatedUsage = getUsageValueAccordingToLastQuotaUsageEntryAndLastQuotaBalance(accountId, domainId, startDate, endDate, aggregatedUsage, accountToString);
+
+ for (QuotaUsageVO quotaUsage : accountQuotaUsages) {
+ Date quotaUsageStartDate = quotaUsage.getStartDate();
+ Date quotaUsageEndDate = quotaUsage.getEndDate();
+ BigDecimal quotaUsed = quotaUsage.getQuotaUsed();
+
+ if (quotaUsed.equals(BigDecimal.ZERO)) {
+ aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, quotaUsageStartDate, quotaUsageEndDate, accountToString));
continue;
}
- if (startDate.compareTo(entry.getStartDate()) != 0) {
- saveQuotaBalance(account, aggrUsage, endDate);
- //New balance entry
- aggrUsage = new BigDecimal(0);
- startDate = entry.getStartDate();
- endDate = entry.getEndDate();
-
- QuotaBalanceVO lastRealBalanceEntry = _quotaBalanceDao.findLastBalanceEntry(account.getAccountId(), account.getDomainId(), endDate);
- Date lastBalanceDate = new Date(0);
- if (lastRealBalanceEntry != null) {
- lastBalanceDate = lastRealBalanceEntry.getUpdatedOn();
- aggrUsage = aggrUsage.add(lastRealBalanceEntry.getCreditBalance());
- }
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Getting Balance" + account.getAccountName() + ",Balance entry=" + aggrUsage + " on Date=" + endDate);
- }
- aggrUsage = aggrUsage.add(aggregateCreditBetweenDates(account, lastBalanceDate, endDate));
+ if (startDate.compareTo(quotaUsageStartDate) == 0) {
+ aggregatedUsage = aggregatedUsage.subtract(quotaUsed);
+ continue;
}
- aggrUsage = aggrUsage.subtract(entry.getQuotaUsed());
- }
- saveQuotaBalance(account, aggrUsage, endDate);
- // update quota_balance
- saveQuotaAccount(account, aggrUsage, endDate);
+ _quotaBalanceDao.saveQuotaBalance(new QuotaBalanceVO(accountId, domainId, aggregatedUsage, endDate));
+
+ aggregatedUsage = BigDecimal.ZERO;
+ startDate = quotaUsageStartDate;
+ endDate = quotaUsageEndDate;
+
+ QuotaBalanceVO lastRealBalanceEntry = _quotaBalanceDao.findLastBalanceEntry(accountId, domainId, endDate);
+ Date lastBalanceDate = new Date(0);
+
+ if (lastRealBalanceEntry != null) {
+ lastBalanceDate = lastRealBalanceEntry.getUpdatedOn();
+ aggregatedUsage = aggregatedUsage.add(lastRealBalanceEntry.getCreditBalance());
+ }
+
+ aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, lastBalanceDate, endDate, accountToString));
+ aggregatedUsage = aggregatedUsage.subtract(quotaUsed);
+ }
+
+ _quotaBalanceDao.saveQuotaBalance(new QuotaBalanceVO(accountId, domainId, aggregatedUsage, endDate));
+ saveQuotaAccount(accountId, aggregatedUsage, endDate);
}
- private QuotaBalanceVO saveQuotaBalance(final AccountVO account, final BigDecimal aggrUsage, final Date endDate) {
- QuotaBalanceVO newBalance = new QuotaBalanceVO(account.getAccountId(), account.getDomainId(), aggrUsage, endDate);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Saving Balance" + newBalance);
- }
- return _quotaBalanceDao.saveQuotaBalance(newBalance);
- }
+ protected BigDecimal getUsageValueAccordingToLastQuotaUsageEntryAndLastQuotaBalance(long accountId, long domainId, Date startDate, Date endDate, BigDecimal aggregatedUsage,
+ String accountToString) {
+ QuotaUsageVO lastQuotaUsage = _quotaUsageDao.findLastQuotaUsageEntry(accountId, domainId, startDate);
- private boolean saveQuotaAccount(final AccountVO account, final BigDecimal aggrUsage, final Date endDate) {
- // update quota_accounts
- QuotaAccountVO quota_account = _quotaAcc.findByIdQuotaAccount(account.getAccountId());
+ if (lastQuotaUsage == null) {
+ aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, new Date(0), startDate, accountToString));
+ QuotaBalanceVO firstBalance = new QuotaBalanceVO(accountId, domainId, aggregatedUsage, startDate);
- if (quota_account == null) {
- quota_account = new QuotaAccountVO(account.getAccountId());
- quota_account.setQuotaBalance(aggrUsage);
- quota_account.setQuotaBalanceDate(endDate);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug(quota_account);
- }
- _quotaAcc.persistQuotaAccount(quota_account);
- return true;
+ s_logger.debug(String.format("Persisting the first quota balance [%s] for account [%s].", firstBalance, accountToString));
+ _quotaBalanceDao.saveQuotaBalance(firstBalance);
} else {
- quota_account.setQuotaBalance(aggrUsage);
- quota_account.setQuotaBalanceDate(endDate);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug(quota_account);
+ QuotaBalanceVO lastRealBalance = _quotaBalanceDao.findLastBalanceEntry(accountId, domainId, endDate);
+
+ if (lastRealBalance != null) {
+ aggregatedUsage = aggregatedUsage.add(lastRealBalance.getCreditBalance());
+ aggregatedUsage = aggregatedUsage.add(aggregateCreditBetweenDates(accountId, domainId, lastRealBalance.getUpdatedOn(), endDate, accountToString));
+ } else {
+ s_logger.warn(String.format("Account [%s] has quota usage entries, however it does not have a quota balance.", accountToString));
}
- return _quotaAcc.updateQuotaAccount(account.getAccountId(), quota_account);
}
+
+ return aggregatedUsage;
}
- private BigDecimal aggregateCreditBetweenDates(final AccountVO account, final Date startDate, final Date endDate) {
- BigDecimal aggrUsage = new BigDecimal(0);
- List creditsReceived = null;
- creditsReceived = _quotaBalanceDao.findCreditBalance(account.getAccountId(), account.getDomainId(), startDate, endDate);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Credit entries count " + creditsReceived.size() + " on Before Date=" + endDate);
+ protected void saveQuotaAccount(long accountId, BigDecimal aggregatedUsage, Date endDate) {
+ QuotaAccountVO quotaAccount = _quotaAcc.findByIdQuotaAccount(accountId);
+
+ if (quotaAccount != null) {
+ quotaAccount.setQuotaBalance(aggregatedUsage);
+ quotaAccount.setQuotaBalanceDate(endDate);
+ _quotaAcc.updateQuotaAccount(accountId, quotaAccount);
+ return;
}
- if (creditsReceived != null) {
- for (QuotaBalanceVO credit : creditsReceived) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Credit entry found " + credit);
- s_logger.debug("Total = " + aggrUsage);
- }
- aggrUsage = aggrUsage.add(credit.getCreditBalance());
- }
+
+ quotaAccount = new QuotaAccountVO(accountId);
+ quotaAccount.setQuotaBalance(aggregatedUsage);
+ quotaAccount.setQuotaBalanceDate(endDate);
+ _quotaAcc.persistQuotaAccount(quotaAccount);
+ }
+
+ protected BigDecimal aggregateCreditBetweenDates(Long accountId, Long domainId, Date startDate, Date endDate, String accountToString) {
+ List creditsReceived = _quotaBalanceDao.findCreditBalance(accountId, domainId, startDate, endDate);
+ s_logger.debug(String.format("Account [%s] has [%s] credit entries before [%s].", accountToString, creditsReceived.size(), endDate));
+
+ BigDecimal aggregatedUsage = BigDecimal.ZERO;
+
+ s_logger.debug(String.format("Aggregating the account [%s] credit entries before [%s].", accountToString, endDate));
+
+ for (QuotaBalanceVO credit : creditsReceived) {
+ aggregatedUsage = aggregatedUsage.add(credit.getCreditBalance());
}
- return aggrUsage;
+
+ s_logger.debug(String.format("The aggregation of the account [%s] credit entries before [%s] resulted in the value [%s].", accountToString, endDate, aggregatedUsage));
+
+ return aggregatedUsage;
}
@Override
public boolean calculateQuotaUsage() {
List accounts = _accountDao.listAll();
+ String accountsToString = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(accounts, "id", "uuid", "accountName", "domainId");
+
+ s_logger.info(String.format("Starting quota usage calculation for accounts [%s].", accountsToString));
+
+ Map, Boolean>> mapQuotaTariffsPerUsageType = createMapQuotaTariffsPerUsageType();
+
for (AccountVO account : accounts) {
- Pair, Integer> usageRecords = _usageDao.getUsageRecordsPendingQuotaAggregation(account.getAccountId(), account.getDomainId());
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Usage entries size = " + usageRecords.second().intValue() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
+ List usageRecords = getPendingUsageRecordsForQuotaAggregation(account);
+
+ if (usageRecords == null) {
+ s_logger.debug(String.format("Account [%s] does not have pending usage records. Skipping to next account.", account.reflectionToString()));
+ continue;
}
- List quotaListForAccount = aggregatePendingQuotaRecordsForAccount(account, usageRecords);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Quota entries size = " + quotaListForAccount.size() + ", accId" + account.getAccountId() + ", domId" + account.getDomainId());
- }
- processQuotaBalanceForAccount(account, quotaListForAccount);
+
+ List quotaUsages = createQuotaUsagesAccordingToQuotaTariffs(account, usageRecords, mapQuotaTariffsPerUsageType);
+ processQuotaBalanceForAccount(account, quotaUsages);
}
+
+ s_logger.info(String.format("Finished quota usage calculation for accounts [%s].", accountsToString));
+
return true;
}
- public QuotaUsageVO updateQuotaDiskUsage(UsageVO usageRecord, final int quotaType) {
- QuotaUsageVO quota_usage = null;
- QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(quotaType, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
- BigDecimal quotaUsgage;
- BigDecimal onehourcostpergb;
- BigDecimal noofgbinuse;
- onehourcostpergb = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- noofgbinuse = new BigDecimal(usageRecord.getSize()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
- quotaUsgage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostpergb).multiply(noofgbinuse);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), usageRecord.getUsageType(),
- quotaUsgage, usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
+ protected List getPendingUsageRecordsForQuotaAggregation(AccountVO account) {
+ Long accountId = account.getId();
+ Long domainId = account.getDomainId();
+
+ Pair, Integer> usageRecords = _usageDao.listUsageRecordsPendingForQuotaAggregation(accountId, domainId);
+
+ List records = usageRecords.first();
+ if (CollectionUtils.isEmpty(records)) {
+ return null;
}
- usageRecord.setQuotaCalculated(1);
- _usageDao.persistUsage(usageRecord);
- return quota_usage;
+
+ s_logger.debug(String.format("Retrieved [%s] pending usage records for account [%s].", usageRecords.second(), account.reflectionToString()));
+
+ return records;
}
- public List updateQuotaRunningVMUsage(UsageVO usageRecord) {
- List quotalist = new ArrayList();
- QuotaUsageVO quota_usage;
- BigDecimal cpuquotausgage, speedquotausage, memoryquotausage, vmusage;
- BigDecimal onehourcostpercpu, onehourcostper100mhz, onehourcostper1mb, onehourcostforvmusage;
- BigDecimal rawusage;
- // get service offering details
- ServiceOfferingVO serviceoffering = _serviceOfferingDao.findServiceOffering(usageRecord.getVmInstanceId(), usageRecord.getOfferingId());
- if (serviceoffering == null) {
- return quotalist;
- }
- rawusage = new BigDecimal(usageRecord.getRawUsage());
+ protected List createQuotaUsagesAccordingToQuotaTariffs(AccountVO account, List usageRecords,
+ Map, Boolean>> mapQuotaTariffsPerUsageType) {
+ String accountToString = account.reflectionToString();
+ s_logger.info(String.format("Calculating quota usage of [%s] usage records for account [%s].", usageRecords.size(), accountToString));
- QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_NUMBER, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0 && serviceoffering.getCpu() != null) {
- BigDecimal cpu = new BigDecimal(serviceoffering.getCpu());
- onehourcostpercpu = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- cpuquotausgage = rawusage.multiply(onehourcostpercpu).multiply(cpu);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_NUMBER,
- cpuquotausgage, usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
- quotalist.add(quota_usage);
- }
- tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.CPU_CLOCK_RATE, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0 && serviceoffering.getSpeed() != null) {
- BigDecimal speed = new BigDecimal(serviceoffering.getSpeed()*serviceoffering.getCpu() / 100.00);
- onehourcostper100mhz = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- speedquotausage = rawusage.multiply(onehourcostper100mhz).multiply(speed);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.CPU_CLOCK_RATE,
- speedquotausage, usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
- quotalist.add(quota_usage);
- }
- tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.MEMORY, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0 && serviceoffering.getRamSize() != null) {
- BigDecimal memory = new BigDecimal(serviceoffering.getRamSize());
- onehourcostper1mb = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- memoryquotausage = rawusage.multiply(onehourcostper1mb).multiply(memory);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.MEMORY, memoryquotausage,
- usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
- quotalist.add(quota_usage);
- }
- tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.RUNNING_VM, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
- onehourcostforvmusage = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- vmusage = rawusage.multiply(onehourcostforvmusage);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.RUNNING_VM, vmusage,
- usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
- quotalist.add(quota_usage);
+ List> pairsUsageAndQuotaUsage = new ArrayList<>();
+
+ try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
+ for (UsageVO usageRecord : usageRecords) {
+ int usageType = usageRecord.getUsageType();
+
+ if (usageTypesToAvoidCalculation.contains(usageType)) {
+ s_logger.debug(String.format("Considering usage record [%s] as calculated and skipping it because the calculation of the types [%s] has not been implemented yet.",
+ usageRecord.toString(), usageTypesToAvoidCalculation));
+ pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null));
+ continue;
+ }
+
+ Pair, Boolean> pairQuotaTariffsPerUsageTypeAndHasActivationRule = mapQuotaTariffsPerUsageType.get(usageType);
+ List quotaTariffs = pairQuotaTariffsPerUsageTypeAndHasActivationRule.first();
+ boolean hasAnyQuotaTariffWithActivationRule = pairQuotaTariffsPerUsageTypeAndHasActivationRule.second();
+
+ BigDecimal aggregatedQuotaTariffsValue = aggregateQuotaTariffsValues(usageRecord, quotaTariffs, hasAnyQuotaTariffWithActivationRule, jsInterpreter, accountToString);
+
+ QuotaUsageVO quotaUsage = createQuotaUsageAccordingToUsageUnit(usageRecord, aggregatedQuotaTariffsValue, accountToString);
+
+ pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, quotaUsage));
+ }
+ } catch (Exception e) {
+ s_logger.error(String.format("Failed to calculate the quota usage for account [%s] due to [%s].", accountToString, e.getMessage()), e);
+ return new ArrayList<>();
}
- usageRecord.setQuotaCalculated(1);
- _usageDao.persistUsage(usageRecord);
- return quotalist;
+ return persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(pairsUsageAndQuotaUsage);
}
- public QuotaUsageVO updateQuotaAllocatedVMUsage(UsageVO usageRecord) {
- QuotaUsageVO quota_usage = null;
- QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(QuotaTypes.ALLOCATED_VM, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
- BigDecimal vmusage;
- BigDecimal onehourcostforvmusage;
- onehourcostforvmusage = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- vmusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcostforvmusage);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), QuotaTypes.ALLOCATED_VM, vmusage,
- usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
+ protected List persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(List> pairsUsageAndQuotaUsage) {
+ List quotaUsages = new ArrayList<>();
+
+ for (Pair pairUsageAndQuotaUsage : pairsUsageAndQuotaUsage) {
+ UsageVO usageVo = pairUsageAndQuotaUsage.first();
+ usageVo.setQuotaCalculated(1);
+ _usageDao.persistUsage(usageVo);
+
+ QuotaUsageVO quotaUsageVo = pairUsageAndQuotaUsage.second();
+ if (quotaUsageVo != null) {
+ _quotaUsageDao.persistQuotaUsage(quotaUsageVo);
+ quotaUsages.add(quotaUsageVo);
+ }
}
- usageRecord.setQuotaCalculated(1);
- _usageDao.persistUsage(usageRecord);
- return quota_usage;
+ return quotaUsages;
}
- public QuotaUsageVO updateQuotaRaw(UsageVO usageRecord, final int ruleType) {
- QuotaUsageVO quota_usage = null;
- QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(ruleType, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
- BigDecimal ruleusage;
- BigDecimal onehourcost;
- onehourcost = tariff.getCurrencyValue().divide(s_hoursInMonth, 8, RoundingMode.HALF_DOWN);
- ruleusage = new BigDecimal(usageRecord.getRawUsage()).multiply(onehourcost);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), ruleType, ruleusage,
- usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
+ protected BigDecimal aggregateQuotaTariffsValues(UsageVO usageRecord, List quotaTariffs, boolean hasAnyQuotaTariffWithActivationRule,
+ JsInterpreter jsInterpreter, String accountToString) {
+ String usageRecordToString = usageRecord.toString();
+ s_logger.debug(String.format("Validating usage record [%s] for account [%s] against [%s] quota tariffs.", usageRecordToString, accountToString,
+ quotaTariffs.size()));
+
+ PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord);
+ BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO;
+
+ for (QuotaTariffVO quotaTariff : quotaTariffs) {
+ if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) {
+ aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables));
+ }
}
- usageRecord.setQuotaCalculated(1);
- _usageDao.persistUsage(usageRecord);
- return quota_usage;
+ s_logger.debug(String.format("The aggregation of the quota tariffs resulted in the value [%s] for the usage record [%s]. We will use this value to calculate the final"
+ + " usage value.", aggregatedQuotaTariffsValue, usageRecordToString));
+
+ return aggregatedQuotaTariffsValue;
}
- public QuotaUsageVO updateQuotaNetwork(UsageVO usageRecord, final int transferType) {
- QuotaUsageVO quota_usage = null;
- QuotaTariffVO tariff = _quotaTariffDao.findTariffPlanByUsageType(transferType, usageRecord.getEndDate());
- if (tariff != null && tariff.getCurrencyValue().compareTo(BigDecimal.ZERO) != 0) {
- BigDecimal onegbcost;
- BigDecimal rawusageingb;
- BigDecimal networkusage;
- onegbcost = tariff.getCurrencyValue();
- rawusageingb = new BigDecimal(usageRecord.getRawUsage()).divide(s_gb, 8, RoundingMode.HALF_EVEN);
- networkusage = rawusageingb.multiply(onegbcost);
- quota_usage = new QuotaUsageVO(usageRecord.getId(), usageRecord.getZoneId(), usageRecord.getAccountId(), usageRecord.getDomainId(), transferType, networkusage,
- usageRecord.getStartDate(), usageRecord.getEndDate());
- _quotaUsageDao.persistQuotaUsage(quota_usage);
+ protected PresetVariables getPresetVariables(boolean hasAnyQuotaTariffWithActivationRule, UsageVO usageRecord) {
+ if (hasAnyQuotaTariffWithActivationRule) {
+ return presetVariableHelper.getPresetVariables(usageRecord);
}
- usageRecord.setQuotaCalculated(1);
- _usageDao.persistUsage(usageRecord);
- return quota_usage;
+ return null;
+ }
+
+ /**
+ * Returns the quota tariff value according to the result of the activation rule.
+ *
+ *
If the activation rule is null or empty, returns {@link QuotaTariffVO#getCurrencyValue()}.
+ *
If the activation rule result in a number, returns it.
+ *
If the activation rule result in a boolean and its is true, returns {@link QuotaTariffVO#getCurrencyValue()}.
+ *
If the activation rule result in a boolean and its is false, returns {@link BigDecimal#ZERO}.
+ *
If the activation rule result in something else, returns {@link BigDecimal#ZERO}.
+ *
+ */
+ protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables) {
+ String activationRule = quotaTariff.getActivationRule();
+ BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue();
+ String quotaTariffToString = quotaTariff.toString();
+
+ if (StringUtils.isEmpty(activationRule)) {
+ s_logger.debug(String.format("Quota tariff [%s] does not have an activation rule, therefore we will use the quota tariff value [%s] in the calculation.",
+ quotaTariffToString, quotaTariffValue));
+ return quotaTariffValue;
+ }
+
+ injectPresetVariablesIntoJsInterpreter(jsInterpreter, presetVariables);
+
+ String scriptResult = jsInterpreter.executeScript(activationRule).toString();
+
+ if (NumberUtils.isParsable(scriptResult)) {
+ s_logger.debug(String.format("The script [%s] of quota tariff [%s] had a numeric value [%s], therefore we will use it in the calculation.", activationRule,
+ quotaTariffToString, scriptResult));
+
+ return new BigDecimal(scriptResult);
+ }
+
+ if (BooleanUtils.toBoolean(scriptResult)) {
+ s_logger.debug(String.format("The script [%s] of quota tariff [%s] had a true boolean result, therefore we will use the quota tariff's value [%s] in the calculation.",
+ activationRule, quotaTariffToString, quotaTariffValue));
+
+ return quotaTariffValue;
+ }
+
+ s_logger.debug(String.format("The script [%s] of quota tariff [%s] had the result [%s], therefore we will not use this quota tariff in the calculation.", activationRule,
+ quotaTariffToString, quotaTariffValue));
+
+ return BigDecimal.ZERO;
+ }
+
+ /**
+ * Injects the preset variables into the JS interpreter.
+ */
+ protected void injectPresetVariablesIntoJsInterpreter(JsInterpreter jsInterpreter, PresetVariables presetVariables) {
+ jsInterpreter.discardCurrentVariables();
+
+ jsInterpreter.injectVariable("account", presetVariables.getAccount().toString());
+ jsInterpreter.injectVariable("domain", presetVariables.getDomain().toString());
+
+ GenericPresetVariable project = presetVariables.getProject();
+ if (project != null) {
+ jsInterpreter.injectVariable("project", project.toString());
+
+ }
+
+ jsInterpreter.injectVariable("resourceType", presetVariables.getResourceType());
+ jsInterpreter.injectVariable("value", presetVariables.getValue().toString());
+ jsInterpreter.injectVariable("zone", presetVariables.getZone().toString());
+ }
+
+ /**
+ * Verifies if the quota tariff should be applied on the usage record according to their respectively start and end date.
+ */
+ protected boolean isQuotaTariffInPeriodToBeApplied(UsageVO usageRecord, QuotaTariffVO quotaTariff, String accountToString) {
+ Date usageRecordStartDate = usageRecord.getStartDate();
+ Date usageRecordEndDate = usageRecord.getEndDate();
+ Date quotaTariffStartDate = quotaTariff.getEffectiveOn();
+ Date quotaTariffEndDate = quotaTariff.getEndDate();
+
+ if ((quotaTariffEndDate != null && usageRecordStartDate.after(quotaTariffEndDate)) || usageRecordEndDate.before(quotaTariffStartDate)) {
+ s_logger.debug(String.format("Not applying quota tariff [%s] in usage record [%s] of account [%s] due to it is out of the period to be applied. Period of the usage"
+ + " record [startDate: %s, endDate: %s], period of the quota tariff [startDate: %s, endDate: %s].", quotaTariff, usageRecord.toString(), accountToString,
+ DateUtil.getOutputString(usageRecordStartDate), DateUtil.getOutputString(usageRecordEndDate), DateUtil.getOutputString(quotaTariffStartDate),
+ DateUtil.getOutputString(quotaTariffEndDate)));
+
+ return false;
+ }
+
+ return true;
+ }
+
+ protected Map, Boolean>> createMapQuotaTariffsPerUsageType() {
+ List quotaTariffs = _quotaTariffDao.listQuotaTariffs(null, null, null, null, null, false, null, null).first();
+
+ Map, Boolean>> mapQuotaTariffsPerUsageType = new HashMap<>();
+
+ for (Map.Entry entry : QuotaTypes.listQuotaTypes().entrySet()) {
+ int quotaType = entry.getKey();
+
+ List quotaTariffsFiltered = quotaTariffs.stream().filter(quotaTariff -> quotaTariff.getUsageType() == quotaType).collect(Collectors.toList());
+ Boolean hasAnyQuotaTariffWithActivationRule = quotaTariffsFiltered.stream().anyMatch(quotaTariff -> StringUtils.isNotEmpty(quotaTariff.getActivationRule()));
+
+ mapQuotaTariffsPerUsageType.put(quotaType, new Pair<>(quotaTariffsFiltered, hasAnyQuotaTariffWithActivationRule));
+ }
+
+ return mapQuotaTariffsPerUsageType;
+ }
+
+ protected QuotaUsageVO createQuotaUsageAccordingToUsageUnit(UsageVO usageRecord, BigDecimal aggregatedQuotaTariffsValue, String accountToString) {
+ String usageRecordToString = usageRecord.toString();
+
+ if (aggregatedQuotaTariffsValue.equals(BigDecimal.ZERO)) {
+ s_logger.debug(String.format("Usage record [%s] for account [%s] does not have quota tariffs to be calculated, therefore we will mark it as calculated.",
+ usageRecordToString, accountToString));
+ return null;
+ }
+
+ QuotaTypes quotaType = QuotaTypes.listQuotaTypes().get(usageRecord.getUsageType());
+ String quotaUnit = quotaType.getQuotaUnit();
+
+ s_logger.debug(String.format("Calculating value of usage record [%s] for account [%s] according to the aggregated quota tariffs value [%s] and its usage unit [%s].",
+ usageRecordToString, accountToString, aggregatedQuotaTariffsValue, quotaUnit));
+
+ BigDecimal usageValue = getUsageValueAccordingToUsageUnitType(usageRecord, aggregatedQuotaTariffsValue, quotaUnit);
+
+ s_logger.debug(String.format("The calculation of the usage record [%s] for account [%s] according to the aggregated quota tariffs value [%s] and its usage unit [%s] "
+ + "resulted in the value [%s].", usageRecordToString, accountToString, aggregatedQuotaTariffsValue, quotaUnit, usageValue));
+
+ QuotaUsageVO quotaUsageVo = new QuotaUsageVO();
+ quotaUsageVo.setUsageItemId(usageRecord.getId());
+ quotaUsageVo.setZoneId(usageRecord.getZoneId());
+ quotaUsageVo.setAccountId(usageRecord.getAccountId());
+ quotaUsageVo.setDomainId(usageRecord.getDomainId());
+ quotaUsageVo.setUsageType(quotaType.getQuotaType());
+ quotaUsageVo.setQuotaUsed(usageValue);
+ quotaUsageVo.setStartDate(usageRecord.getStartDate());
+ quotaUsageVo.setEndDate(usageRecord.getEndDate());
+
+ return quotaUsageVo;
+ }
+
+ protected BigDecimal getUsageValueAccordingToUsageUnitType(UsageVO usageRecord, BigDecimal aggregatedQuotaTariffsValue, String quotaUnit) {
+ BigDecimal rawUsage = BigDecimal.valueOf(usageRecord.getRawUsage());
+ BigDecimal costPerHour = aggregatedQuotaTariffsValue.divide(s_hoursInMonth, 8, RoundingMode.HALF_EVEN);
+
+ switch (UsageUnitTypes.getByDescription(quotaUnit)) {
+ case COMPUTE_MONTH:
+ case IP_MONTH:
+ case POLICY_MONTH:
+ return rawUsage.multiply(costPerHour);
+
+ case GB:
+ BigDecimal rawUsageInGb = rawUsage.divide(GiB_DECIMAL, 8, RoundingMode.HALF_EVEN);
+ return rawUsageInGb.multiply(aggregatedQuotaTariffsValue);
+
+ case GB_MONTH:
+ BigDecimal gbInUse = BigDecimal.valueOf(usageRecord.getSize()).divide(GiB_DECIMAL, 8, RoundingMode.HALF_EVEN);
+ return rawUsage.multiply(costPerHour).multiply(gbInUse);
+
+ default:
+ return BigDecimal.ZERO;
+ }
}
@Override
public boolean isLockable(AccountVO account) {
- return (account.getType() == Account.Type.NORMAL || account.getType() == Account.Type.DOMAIN_ADMIN);
+ return lockablesAccountTypes.contains(account.getType());
}
}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java
new file mode 100644
index 00000000000..749bd9eff5a
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java
@@ -0,0 +1,32 @@
+// 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.activationrule.presetvariables;
+
+public class Account extends GenericPresetVariable{
+ private Role role;
+
+ public Role getRole() {
+ return role;
+ }
+
+ public void setRole(Role role) {
+ this.role = role;
+ fieldNamesToIncludeInToString.add("role");
+ }
+
+}
\ No newline at end of file
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java
new file mode 100644
index 00000000000..457e71a141f
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java
@@ -0,0 +1,33 @@
+/*
+ * 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.activationrule.presetvariables;
+
+public class BackupOffering extends GenericPresetVariable {
+ private String externalId;
+
+ public String getExternalId() {
+ return externalId;
+ }
+
+ public void setExternalId(String externalId) {
+ this.externalId = externalId;
+ fieldNamesToIncludeInToString.add("externalId");
+ }
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java
new file mode 100644
index 00000000000..b42c32a584e
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java
@@ -0,0 +1,32 @@
+// 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.activationrule.presetvariables;
+
+public class ComputeOffering extends GenericPresetVariable {
+ private boolean customized;
+
+ public boolean isCustomized() {
+ return customized;
+ }
+
+ public void setCustomized(boolean customized) {
+ this.customized = customized;
+ fieldNamesToIncludeInToString.add("customized");
+ }
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java
new file mode 100644
index 00000000000..d4f335b081c
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java
@@ -0,0 +1,56 @@
+// 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.activationrule.presetvariables;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class ComputingResources {
+ private Integer memory;
+ private Integer cpuNumber;
+ private Integer cpuSpeed;
+
+ public Integer getMemory() {
+ return memory;
+ }
+
+ public void setMemory(Integer memory) {
+ this.memory = memory;
+ }
+
+ public Integer getCpuNumber() {
+ return cpuNumber;
+ }
+
+ public void setCpuNumber(Integer cpuNumber) {
+ this.cpuNumber = cpuNumber;
+ }
+
+ public Integer getCpuSpeed() {
+ return cpuSpeed;
+ }
+
+ public void setCpuSpeed(Integer cpuSpeed) {
+ this.cpuSpeed = cpuSpeed;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+ }
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java
new file mode 100644
index 00000000000..01b702feb1a
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java
@@ -0,0 +1,32 @@
+// 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.activationrule.presetvariables;
+
+public class Domain extends GenericPresetVariable {
+ private String path;
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ fieldNamesToIncludeInToString.add("path");
+ }
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java
new file mode 100644
index 00000000000..b081e57611f
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java
@@ -0,0 +1,52 @@
+// 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.activationrule.presetvariables;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
+
+public class GenericPresetVariable {
+ private String id;
+ private String name;
+ protected transient Set fieldNamesToIncludeInToString = new HashSet<>();
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ fieldNamesToIncludeInToString.add("id");
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ fieldNamesToIncludeInToString.add("name");
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, fieldNamesToIncludeInToString.toArray(new String[0]));
+ }
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java
new file mode 100644
index 00000000000..337131038e8
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java
@@ -0,0 +1,34 @@
+// 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.activationrule.presetvariables;
+
+import java.util.List;
+
+public class Host extends GenericPresetVariable {
+ private List tags;
+
+ public List getTags() {
+ return tags;
+ }
+
+ public void setTags(List tags) {
+ this.tags = tags;
+ fieldNamesToIncludeInToString.add("tags");
+ }
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelper.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelper.java
new file mode 100644
index 00000000000..45bb177c08b
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelper.java
@@ -0,0 +1,653 @@
+// 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.activationrule.presetvariables;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleVO;
+import org.apache.cloudstack.acl.dao.RoleDao;
+import org.apache.cloudstack.backup.BackupOfferingVO;
+import org.apache.cloudstack.backup.dao.BackupOfferingDao;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
+import org.apache.cloudstack.quota.constant.QuotaTypes;
+import org.apache.cloudstack.quota.dao.VmTemplateDao;
+import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
+import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.usage.UsageTypes;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.host.dao.HostTagsDao;
+import com.cloud.offerings.NetworkOfferingVO;
+import com.cloud.offerings.dao.NetworkOfferingDao;
+import com.cloud.server.ResourceTag;
+import com.cloud.server.ResourceTag.ResourceObjectType;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.dao.StoragePoolTagsDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.tags.dao.ResourceTagDao;
+import com.cloud.usage.UsageVO;
+import com.cloud.usage.dao.UsageDao;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmDetailVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.constants.VmDetails;
+import com.cloud.vm.dao.UserVmDetailsDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+
+/**
+ * Retrieves data from the database to inject into the {@link JsInterpreter} to provide more flexibility when defining quota tariffs' activation rules.
+ * As this class is strictly used to retrieve data to inject into the {@link JsInterpreter}, there is no need for logging the retrieved data, because {@link JsInterpreter} already
+ * logs the injected data in TRACE level.
+ */
+
+@Component
+public class PresetVariableHelper {
+ protected Logger logger = Logger.getLogger(PresetVariableHelper.class);
+
+ @Inject
+ AccountDao accountDao;
+
+ @Inject
+ RoleDao roleDao;
+
+ @Inject
+ DomainDao domainDao;
+
+ @Inject
+ DataCenterDao dataCenterDao;
+
+ @Inject
+ UsageDao usageDao;
+
+ @Inject
+ VMInstanceDao vmInstanceDao;
+
+ @Inject
+ HostDao hostDao;
+
+ @Inject
+ HostTagsDao hostTagsDao;
+
+ @Inject
+ GuestOSDao guestOsDao;
+
+ @Inject
+ ServiceOfferingDao serviceOfferingDao;
+
+ @Inject
+ VmTemplateDao vmTemplateDao;
+
+ @Inject
+ ResourceTagDao resourceTagDao;
+
+ @Inject
+ VolumeDao volumeDao;
+
+ @Inject
+ DiskOfferingDao diskOfferingDao;
+
+ @Inject
+ PrimaryDataStoreDao primaryStorageDao;
+
+ @Inject
+ StoragePoolTagsDao storagePoolTagsDao;
+
+ @Inject
+ ImageStoreDao imageStoreDao;
+
+ @Inject
+ SnapshotDao snapshotDao;
+
+ @Inject
+ SnapshotDataStoreDao snapshotDataStoreDao;
+
+ @Inject
+ NetworkOfferingDao networkOfferingDao;
+
+ @Inject
+ VMSnapshotDao vmSnapshotDao;
+
+ @Inject
+ UserVmDetailsDao userVmDetailsDao;
+
+ @Inject
+ BackupOfferingDao backupOfferingDao;
+
+ protected boolean backupSnapshotAfterTakingSnapshot = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
+
+ private List runningAndAllocatedVmUsageTypes = Arrays.asList(UsageTypes.RUNNING_VM, UsageTypes.ALLOCATED_VM);
+ private List templateAndIsoUsageTypes = Arrays.asList(UsageTypes.TEMPLATE, UsageTypes.ISO);
+ private String usageRecordToString = null;
+
+ public PresetVariables getPresetVariables(UsageVO usageRecord) {
+ this.usageRecordToString = usageRecord.toString();
+ PresetVariables presetVariables = new PresetVariables();
+
+ presetVariables.setAccount(getPresetVariableAccount(usageRecord.getAccountId()));
+ setPresetVariableProject(presetVariables);
+
+ presetVariables.setDomain(getPresetVariableDomain(usageRecord.getDomainId()));
+ presetVariables.setResourceType(usageRecord.getType());
+ presetVariables.setValue(getPresetVariableValue(usageRecord));
+ presetVariables.setZone(getPresetVariableZone(usageRecord.getZoneId()));
+
+ return presetVariables;
+ }
+
+ protected void setPresetVariableProject(PresetVariables presetVariables) {
+ Account account = presetVariables.getAccount();
+
+ if (account.getRole() != null) {
+ return;
+ }
+
+ GenericPresetVariable project = new GenericPresetVariable();
+ project.setId(account.getId());
+ project.setName(account.getName());
+
+ presetVariables.setProject(project);
+ }
+
+ protected Account getPresetVariableAccount(Long accountId) {
+ AccountVO accountVo = accountDao.findByIdIncludingRemoved(accountId);
+ validateIfObjectIsNull(accountVo, accountId, "account");
+
+ Account account = new Account();
+ account.setId(accountVo.getUuid());
+ account.setName(accountVo.getName());
+
+ setPresetVariableRoleInAccountIfAccountIsNotAProject(accountVo.getType(), accountVo.getRoleId(), account);
+
+ return account;
+ }
+
+ protected void setPresetVariableRoleInAccountIfAccountIsNotAProject(com.cloud.user.Account.Type accountType, Long roleId, Account account) {
+ if (accountType != com.cloud.user.Account.Type.PROJECT) {
+ account.setRole(getPresetVariableRole(roleId));
+ }
+ }
+
+ protected Role getPresetVariableRole(Long roleId) {
+ RoleVO roleVo = roleDao.findByIdIncludingRemoved(roleId);
+ validateIfObjectIsNull(roleVo, roleId, "role");
+
+ Role role = new Role();
+ role.setId(roleVo.getUuid());
+ role.setName(roleVo.getName());
+ role.setType(roleVo.getRoleType());
+
+ return role;
+ }
+
+ protected Domain getPresetVariableDomain(Long domainId) {
+ DomainVO domainVo = domainDao.findByIdIncludingRemoved(domainId);
+ validateIfObjectIsNull(domainVo, domainId, "domain");
+
+ Domain domain = new Domain();
+ domain.setId(domainVo.getUuid());
+ domain.setName(domainVo.getName());
+ domain.setPath(domainVo.getPath());
+
+ return domain;
+ }
+
+ protected GenericPresetVariable getPresetVariableZone(Long zoneId) {
+ DataCenterVO dataCenterVo = dataCenterDao.findByIdIncludingRemoved(zoneId);
+ validateIfObjectIsNull(dataCenterVo, zoneId, "zone");
+
+ GenericPresetVariable zone = new GenericPresetVariable();
+ zone.setId(dataCenterVo.getUuid());
+ zone.setName(dataCenterVo.getName());
+
+ return zone;
+ }
+
+ protected Value getPresetVariableValue(UsageVO usageRecord) {
+ Long accountId = usageRecord.getAccountId();
+ int usageType = usageRecord.getUsageType();
+
+ Value value = new Value();
+
+ value.setAccountResources(getPresetVariableAccountResources(usageRecord, accountId, usageType));
+ loadPresetVariableValueForRunningAndAllocatedVm(usageRecord, value);
+ loadPresetVariableValueForVolume(usageRecord, value);
+ loadPresetVariableValueForTemplateAndIso(usageRecord, value);
+ loadPresetVariableValueForSnapshot(usageRecord, value);
+ loadPresetVariableValueForNetworkOffering(usageRecord, value);
+ loadPresetVariableValueForVmSnapshot(usageRecord, value);
+ loadPresetVariableValueForBackup(usageRecord, value);
+
+ return value;
+ }
+
+ /**
+ * Retrieves a list of zone and domain IDs of the records allocated in the same period and same usage type of the usage record.
+ */
+ protected List getPresetVariableAccountResources(UsageVO usageRecord, Long accountId, int usageType) {
+ Date startDate = usageRecord.getStartDate();
+ Date endDate = usageRecord.getEndDate();
+
+ List> pairResources = usageDao.listAccountResourcesInThePeriod(accountId, usageType, startDate, endDate);
+
+ List resourcesInThePeriod = new ArrayList<>();
+
+ for (Pair pairResource : pairResources) {
+ Resource resource = new Resource();
+ resource.setZoneId(pairResource.first());
+ resource.setDomainId(pairResource.second());
+
+ resourcesInThePeriod.add(resource);
+ }
+
+ return resourcesInThePeriod;
+ }
+
+ protected void loadPresetVariableValueForRunningAndAllocatedVm(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+
+ if (!runningAndAllocatedVmUsageTypes.contains(usageType)) {
+ logNotLoadingMessageInTrace("running/allocated VM", usageType);
+ return;
+ }
+
+ Long vmId = usageRecord.getUsageId();
+
+ VMInstanceVO vmVo = vmInstanceDao.findByIdIncludingRemoved(vmId);
+ validateIfObjectIsNull(vmVo, vmId, "VM");
+
+ setPresetVariableHostInValueIfUsageTypeIsRunningVm(value, usageType, vmVo);
+
+ value.setId(vmVo.getUuid());
+ value.setName(vmVo.getHostName());
+ value.setOsName(getPresetVariableValueOsName(vmVo.getGuestOSId()));
+
+ setPresetVariableValueServiceOfferingAndComputingResources(value, usageType, vmVo);
+
+ value.setTags(getPresetVariableValueResourceTags(vmId, ResourceObjectType.UserVm));
+ value.setTemplate(getPresetVariableValueTemplate(vmVo.getTemplateId()));
+ }
+
+ protected void logNotLoadingMessageInTrace(String resource, int usageType) {
+ logger.trace(String.format("Not loading %s preset variables because the usage record [%s] is of type [%s].", resource, usageRecordToString,
+ QuotaTypes.listQuotaTypes().get(usageType).getQuotaName()));
+ }
+
+ protected void setPresetVariableHostInValueIfUsageTypeIsRunningVm(Value value, int quotaType, VMInstanceVO vmVo) {
+ if (quotaType != UsageTypes.RUNNING_VM) {
+ return;
+ }
+
+ Long hostId = vmVo.getHostId();
+ if (hostId == null) {
+ hostId = vmVo.getLastHostId();
+ }
+
+ value.setHost(getPresetVariableValueHost(hostId));
+ }
+
+ protected Host getPresetVariableValueHost(Long hostId) {
+ HostVO hostVo = hostDao.findByIdIncludingRemoved(hostId);
+ validateIfObjectIsNull(hostVo, hostId, "host");
+
+ Host host = new Host();
+ host.setId(hostVo.getUuid());
+ host.setName(hostVo.getName());
+ host.setTags(hostTagsDao.getHostTags(hostId));
+
+ return host;
+ }
+
+ protected String getPresetVariableValueOsName(Long guestOsId) {
+ GuestOSVO guestOsVo = guestOsDao.findByIdIncludingRemoved(guestOsId);
+ validateIfObjectIsNull(guestOsVo, guestOsId, "guest OS");
+
+ return guestOsVo.getDisplayName();
+ }
+
+ protected ComputeOffering getPresetVariableValueComputeOffering(ServiceOfferingVO serviceOfferingVo) {
+ ComputeOffering computeOffering = new ComputeOffering();
+ computeOffering.setId(serviceOfferingVo.getUuid());
+ computeOffering.setName(serviceOfferingVo.getName());
+ computeOffering.setCustomized(serviceOfferingVo.isDynamic());
+
+ return computeOffering;
+ }
+
+
+ protected void setPresetVariableValueServiceOfferingAndComputingResources(Value value, int usageType, VMInstanceVO vmVo) {
+ long computeOfferingId = vmVo.getServiceOfferingId();
+ ServiceOfferingVO serviceOfferingVo = serviceOfferingDao.findByIdIncludingRemoved(computeOfferingId);
+ validateIfObjectIsNull(serviceOfferingVo, computeOfferingId, "compute offering");
+ value.setComputeOffering(getPresetVariableValueComputeOffering(serviceOfferingVo));
+
+ if (usageType == UsageTypes.RUNNING_VM) {
+ value.setComputingResources(getPresetVariableValueComputingResource(vmVo, serviceOfferingVo));
+ }
+ }
+
+ protected ComputingResources getPresetVariableValueComputingResource(VMInstanceVO vmVo, ServiceOfferingVO serviceOfferingVo) {
+ ComputingResources computingResources = new ComputingResources();
+ computingResources.setMemory(serviceOfferingVo.getRamSize());
+ computingResources.setCpuNumber(serviceOfferingVo.getCpu());
+ computingResources.setCpuSpeed(serviceOfferingVo.getSpeed());
+
+ if (serviceOfferingVo.isDynamic()) {
+ List details = userVmDetailsDao.listDetails(vmVo.getId());
+
+ computingResources.setMemory(getDetailByName(details, VmDetails.MEMORY.getName(), computingResources.getMemory()));
+ computingResources.setCpuNumber(getDetailByName(details, VmDetails.CPU_NUMBER.getName(), computingResources.getCpuNumber()));
+ computingResources.setCpuSpeed(getDetailByName(details, VmDetails.CPU_SPEED.getName(), computingResources.getCpuSpeed()));
+ }
+
+ warnIfComputingResourceIsNull(VmDetails.MEMORY.getName(), computingResources.getMemory(), vmVo);
+ warnIfComputingResourceIsNull(VmDetails.CPU_NUMBER.getName(), computingResources.getCpuNumber(), vmVo);
+ warnIfComputingResourceIsNull(VmDetails.CPU_SPEED.getName(), computingResources.getCpuSpeed(), vmVo);
+
+ return computingResources;
+ }
+
+ protected void warnIfComputingResourceIsNull(String name, Integer value, VMInstanceVO vmVo) {
+ if (value == null) {
+ logger.warn(String.format("Could not get %s of %s. Injecting \"value.computingResources.[%s]\" as null.", name, vmVo, name));
+ }
+ }
+
+ protected Integer getDetailByName(List details, String name, Integer defaultValue) {
+ List detailFiltered = details.stream().filter(det -> name.equals(det.getName())).collect(Collectors.toList());
+
+ if (CollectionUtils.isEmpty(detailFiltered)) {
+ return defaultValue;
+ }
+
+ UserVmDetailVO detail = detailFiltered.get(0);
+
+ if (detail.getValue() != null) {
+ return Integer.valueOf(detail.getValue());
+ }
+
+ return defaultValue;
+ }
+ protected GenericPresetVariable getPresetVariableValueTemplate(Long templateId) {
+ VMTemplateVO vmTemplateVo = vmTemplateDao.findByIdIncludingRemoved(templateId);
+ validateIfObjectIsNull(vmTemplateVo, templateId, "template");
+
+ GenericPresetVariable template = new GenericPresetVariable();
+ template.setId(vmTemplateVo.getUuid());
+ template.setName(vmTemplateVo.getName());
+
+ return template;
+ }
+
+ protected Map getPresetVariableValueResourceTags(Long resourceId, ResourceObjectType resourceType) {
+ List extends ResourceTag> listResourceTags = resourceTagDao.listBy(resourceId, resourceType);
+
+ Map mapResourceTags = new HashMap<>();
+ for (ResourceTag resourceTag : listResourceTags) {
+ mapResourceTags.put(resourceTag.getKey(), resourceTag.getValue());
+ }
+
+ return mapResourceTags;
+ }
+
+ protected void loadPresetVariableValueForVolume(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+
+ if (usageType != UsageTypes.VOLUME) {
+ logNotLoadingMessageInTrace("volume", usageType);
+ return;
+ }
+
+ Long volumeId = usageRecord.getUsageId();
+
+ VolumeVO volumeVo = volumeDao.findByIdIncludingRemoved(volumeId);
+ validateIfObjectIsNull(volumeVo, volumeId, "volume");
+
+ value.setDiskOffering(getPresetVariableValueDiskOffering(volumeVo.getDiskOfferingId()));
+ value.setId(volumeVo.getUuid());
+ value.setName(volumeVo.getName());
+ value.setProvisioningType(volumeVo.getProvisioningType());
+
+ Long poolId = volumeVo.getPoolId();
+ if (poolId == null) {
+ logger.debug(String.format("Volume [%s] from usage record [%s] has a NULL pool ID; therefore, the preset variable \"storage\" will not be loaded for this record.",
+ volumeId, usageRecordToString));
+ } else {
+ value.setStorage(getPresetVariableValueStorage(poolId, usageType));
+ }
+
+ value.setTags(getPresetVariableValueResourceTags(volumeId, ResourceObjectType.Volume));
+ value.setSize(ByteScaleUtils.bytesToMib(volumeVo.getSize()));
+ }
+
+ protected GenericPresetVariable getPresetVariableValueDiskOffering(Long diskOfferingId) {
+ DiskOfferingVO diskOfferingVo = diskOfferingDao.findByIdIncludingRemoved(diskOfferingId);
+ validateIfObjectIsNull(diskOfferingVo, diskOfferingId, "disk offering");
+
+ GenericPresetVariable diskOffering = new GenericPresetVariable();
+ diskOffering.setId(diskOfferingVo.getUuid());
+ diskOffering.setName(diskOfferingVo.getName());
+
+ return diskOffering;
+ }
+
+ protected Storage getPresetVariableValueStorage(Long storageId, int usageType) {
+ Storage storage = getSecondaryStorageForSnapshot(storageId, usageType);
+
+ if (storage != null) {
+ return storage;
+ }
+
+ StoragePoolVO storagePoolVo = primaryStorageDao.findByIdIncludingRemoved(storageId);
+ validateIfObjectIsNull(storagePoolVo, storageId, "primary storage");
+
+ storage = new Storage();
+ storage.setId(storagePoolVo.getUuid());
+ storage.setName(storagePoolVo.getName());
+ storage.setScope(storagePoolVo.getScope());
+ storage.setTags(storagePoolTagsDao.getStoragePoolTags(storageId));
+
+ return storage;
+ }
+
+ /**
+ * If the usage type is {@link UsageTypes#SNAPSHOT} and {@link SnapshotInfo#BackupSnapshotAfterTakingSnapshot} is enabled, returns the data from the secondary storage.
+ * Otherwise, returns null.
+ */
+ protected Storage getSecondaryStorageForSnapshot(Long storageId, int usageType) {
+ if (usageType != UsageTypes.SNAPSHOT || !backupSnapshotAfterTakingSnapshot) {
+ return null;
+ }
+
+ ImageStoreVO imageStoreVo = imageStoreDao.findByIdIncludingRemoved(storageId);
+ validateIfObjectIsNull(imageStoreVo, storageId, "secondary storage");
+
+ Storage storage = new Storage();
+ storage.setId(imageStoreVo.getUuid());
+ storage.setName(imageStoreVo.getName());
+ return storage;
+ }
+
+ protected void loadPresetVariableValueForTemplateAndIso(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+ if (!templateAndIsoUsageTypes.contains(usageType)) {
+ logNotLoadingMessageInTrace("template/ISO", usageType);
+ return;
+ }
+
+ Long templateOrIsoId = usageRecord.getUsageId();
+
+ VMTemplateVO vmTemplateVo = vmTemplateDao.findByIdIncludingRemoved(templateOrIsoId);
+ validateIfObjectIsNull(vmTemplateVo, templateOrIsoId, "template/ISO");
+
+ value.setId(vmTemplateVo.getUuid());
+ value.setName(vmTemplateVo.getName());
+ value.setOsName(getPresetVariableValueOsName(vmTemplateVo.getGuestOSId()));
+ value.setTags(getPresetVariableValueResourceTags(templateOrIsoId, usageType == UsageTypes.ISO ? ResourceObjectType.ISO : ResourceObjectType.Template));
+ value.setSize(ByteScaleUtils.bytesToMib(vmTemplateVo.getSize()));
+ }
+
+ protected void loadPresetVariableValueForSnapshot(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+
+ if (usageType != UsageTypes.SNAPSHOT) {
+ logNotLoadingMessageInTrace("snapshot", usageType);
+ return;
+ }
+
+ Long snapshotId = usageRecord.getUsageId();
+
+ SnapshotVO snapshotVo = snapshotDao.findByIdIncludingRemoved(snapshotId);
+ validateIfObjectIsNull(snapshotVo, snapshotId, "snapshot");
+
+ value.setId(snapshotVo.getUuid());
+ value.setName(snapshotVo.getName());
+ value.setSize(ByteScaleUtils.bytesToMib(snapshotVo.getSize()));
+ value.setSnapshotType(Snapshot.Type.values()[snapshotVo.getSnapshotType()]);
+ value.setStorage(getPresetVariableValueStorage(getSnapshotDataStoreId(snapshotId), usageType));
+ value.setTags(getPresetVariableValueResourceTags(snapshotId, ResourceObjectType.Snapshot));
+ }
+
+ /**
+ * If {@link SnapshotInfo#BackupSnapshotAfterTakingSnapshot} is enabled, returns the secondary storage's ID where the snapshot is. Otherwise, returns the primary storage's ID
+ * where the snapshot is.
+ */
+ protected long getSnapshotDataStoreId(Long snapshotId) {
+ if (backupSnapshotAfterTakingSnapshot) {
+ SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image);
+ validateIfObjectIsNull(snapshotStore, snapshotId, "data store for snapshot");
+ return snapshotStore.getDataStoreId();
+ }
+
+ SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findBySnapshot(snapshotId, DataStoreRole.Primary);
+ validateIfObjectIsNull(snapshotStore, snapshotId, "data store for snapshot");
+ return snapshotStore.getDataStoreId();
+ }
+
+ protected void loadPresetVariableValueForNetworkOffering(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+
+ if (usageType != UsageTypes.NETWORK_OFFERING) {
+ logNotLoadingMessageInTrace("network offering", usageType);
+ return;
+ }
+
+ Long networkOfferingId = usageRecord.getOfferingId();
+
+ NetworkOfferingVO networkOfferingVo = networkOfferingDao.findByIdIncludingRemoved(networkOfferingId);
+ validateIfObjectIsNull(networkOfferingVo, networkOfferingId, "network offering");
+
+ value.setId(networkOfferingVo.getUuid());
+ value.setName(networkOfferingVo.getName());
+ value.setTag(networkOfferingVo.getTags());
+ }
+
+ protected void loadPresetVariableValueForVmSnapshot(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+ if (usageType != UsageTypes.VM_SNAPSHOT) {
+ logNotLoadingMessageInTrace("VM snapshot", usageType);
+ return;
+ }
+
+ Long vmSnapshotId = usageRecord.getUsageId();
+
+ VMSnapshotVO vmSnapshotVo = vmSnapshotDao.findByIdIncludingRemoved(vmSnapshotId);
+ validateIfObjectIsNull(vmSnapshotVo, vmSnapshotId, "VM snapshot");
+
+ value.setId(vmSnapshotVo.getUuid());
+ value.setName(vmSnapshotVo.getName());
+ value.setTags(getPresetVariableValueResourceTags(vmSnapshotId, ResourceObjectType.VMSnapshot));
+ value.setVmSnapshotType(vmSnapshotVo.getType());
+ }
+
+ protected void loadPresetVariableValueForBackup(UsageVO usageRecord, Value value) {
+ int usageType = usageRecord.getUsageType();
+ if (usageType != UsageTypes.BACKUP) {
+ logNotLoadingMessageInTrace("Backup", usageType);
+ return;
+ }
+
+ value.setSize(usageRecord.getSize());
+ value.setVirtualSize(usageRecord.getVirtualSize());
+ value.setBackupOffering(getPresetVariableValueBackupOffering(usageRecord.getOfferingId()));
+ }
+
+ protected BackupOffering getPresetVariableValueBackupOffering(Long offeringId) {
+ BackupOfferingVO backupOfferingVo = backupOfferingDao.findByIdIncludingRemoved(offeringId);
+ validateIfObjectIsNull(backupOfferingVo, offeringId, "backup offering");
+
+ BackupOffering backupOffering = new BackupOffering();
+ backupOffering.setId(backupOfferingVo.getUuid());
+ backupOffering.setName(backupOfferingVo.getName());
+ backupOffering.setExternalId(backupOfferingVo.getExternalId());
+
+ return backupOffering;
+ }
+
+ /**
+ * Throws a {@link CloudRuntimeException} if the object is null;
+ */
+ protected void validateIfObjectIsNull(Object object, Long id, String resource) {
+ if (object != null) {
+ return;
+ }
+
+ String message = String.format("Unable to load preset variable [%s] for usage record [%s] due to: [%s] with ID [%s] does not exist.", resource, usageRecordToString,
+ resource, id);
+
+ logger.error(message);
+ throw new CloudRuntimeException(message);
+ }
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java
new file mode 100644
index 00000000000..b47f07540ec
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java
@@ -0,0 +1,76 @@
+// 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.activationrule.presetvariables;
+
+public class PresetVariables {
+
+ private Account account;
+ private Domain domain;
+ private GenericPresetVariable project;
+ private String resourceType;
+ private Value value;
+ private GenericPresetVariable zone;
+
+ public Account getAccount() {
+ return account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ }
+
+ public Domain getDomain() {
+ return domain;
+ }
+
+ public void setDomain(Domain domain) {
+ this.domain = domain;
+ }
+
+ public GenericPresetVariable getProject() {
+ return project;
+ }
+
+ public void setProject(GenericPresetVariable project) {
+ this.project = project;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+ public void setValue(Value value) {
+ this.value = value;
+ }
+
+ public GenericPresetVariable getZone() {
+ return zone;
+ }
+
+ public void setZone(GenericPresetVariable zone) {
+ this.zone = zone;
+ }
+}
\ No newline at end of file
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Resource.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Resource.java
new file mode 100644
index 00000000000..b1dfbacbfcd
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Resource.java
@@ -0,0 +1,48 @@
+// 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.activationrule.presetvariables;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class Resource {
+ private String zoneId;
+ private String domainId;
+
+ public String getZoneId() {
+ return zoneId;
+ }
+
+ public void setZoneId(String zoneId) {
+ this.zoneId = zoneId;
+ }
+
+ public String getDomainId() {
+ return domainId;
+ }
+
+ public void setDomainId(String domainId) {
+ this.domainId = domainId;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+ }
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java
new file mode 100644
index 00000000000..fc4716fc309
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java
@@ -0,0 +1,34 @@
+// 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.activationrule.presetvariables;
+
+import org.apache.cloudstack.acl.RoleType;
+
+public class Role extends GenericPresetVariable {
+ private RoleType type;
+
+ public RoleType getType() {
+ return type;
+ }
+
+ public void setType(RoleType type) {
+ this.type = type;
+ fieldNamesToIncludeInToString.add("type");
+ }
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java
new file mode 100644
index 00000000000..72760bb1ec8
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java
@@ -0,0 +1,46 @@
+// 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.activationrule.presetvariables;
+
+import java.util.List;
+
+import com.cloud.storage.ScopeType;
+
+public class Storage extends GenericPresetVariable {
+ private List tags;
+ private ScopeType scope;
+
+ public List getTags() {
+ return tags;
+ }
+
+ public void setTags(List tags) {
+ this.tags = tags;
+ fieldNamesToIncludeInToString.add("tags");
+ }
+
+ public ScopeType getScope() {
+ return scope;
+ }
+
+ public void setScope(ScopeType scope) {
+ this.scope = scope;
+ fieldNamesToIncludeInToString.add("scope");
+ }
+
+}
\ No newline at end of file
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java
new file mode 100644
index 00000000000..87fefe3a363
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java
@@ -0,0 +1,188 @@
+// 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.activationrule.presetvariables;
+
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.Storage.ProvisioningType;
+import com.cloud.vm.snapshot.VMSnapshot;
+
+public class Value extends GenericPresetVariable {
+ private Host host;
+ private String osName;
+ private List accountResources;
+ private Map tags;
+ private String tag;
+ private Long size;
+ private Long virtualSize;
+ private ProvisioningType provisioningType;
+ private Snapshot.Type snapshotType;
+ private VMSnapshot.Type vmSnapshotType;
+ private ComputeOffering computeOffering;
+ private GenericPresetVariable template;
+ private GenericPresetVariable diskOffering;
+ private Storage storage;
+ private ComputingResources computingResources;
+ private BackupOffering backupOffering;
+
+ public Host getHost() {
+ return host;
+ }
+
+ public void setHost(Host host) {
+ this.host = host;
+ fieldNamesToIncludeInToString.add("host");
+ }
+
+ public String getOsName() {
+ return osName;
+ }
+
+ public void setOsName(String osName) {
+ this.osName = osName;
+ fieldNamesToIncludeInToString.add("osName");
+ }
+
+ public List getAccountResources() {
+ return accountResources;
+ }
+
+ public void setAccountResources(List accountResources) {
+ this.accountResources = accountResources;
+ fieldNamesToIncludeInToString.add("accountResources");
+ }
+
+ public Map getTags() {
+ return tags;
+ }
+
+ public void setTags(Map tags) {
+ this.tags = tags;
+ fieldNamesToIncludeInToString.add("tags");
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public void setTag(String tag) {
+ this.tag = tag;
+ fieldNamesToIncludeInToString.add("tag");
+ }
+
+ public Long getSize() {
+ return size;
+ }
+
+ public void setSize(Long size) {
+ this.size = size;
+ fieldNamesToIncludeInToString.add("size");
+ }
+
+ public ProvisioningType getProvisioningType() {
+ return provisioningType;
+ }
+
+ public void setProvisioningType(ProvisioningType provisioningType) {
+ this.provisioningType = provisioningType;
+ fieldNamesToIncludeInToString.add("provisioningType");
+ }
+
+ public Snapshot.Type getSnapshotType() {
+ return snapshotType;
+ }
+
+ public void setSnapshotType(Snapshot.Type snapshotType) {
+ this.snapshotType = snapshotType;
+ fieldNamesToIncludeInToString.add("snapshotType");
+ }
+
+ public VMSnapshot.Type getVmSnapshotType() {
+ return vmSnapshotType;
+ }
+
+ public void setVmSnapshotType(VMSnapshot.Type vmSnapshotType) {
+ this.vmSnapshotType = vmSnapshotType;
+ fieldNamesToIncludeInToString.add("vmSnapshotType");
+ }
+
+ public ComputeOffering getComputeOffering() {
+ return computeOffering;
+ }
+
+ public void setComputeOffering(ComputeOffering computeOffering) {
+ this.computeOffering = computeOffering;
+ fieldNamesToIncludeInToString.add("computeOffering");
+ }
+
+ public GenericPresetVariable getTemplate() {
+ return template;
+ }
+
+ public void setTemplate(GenericPresetVariable template) {
+ this.template = template;
+ fieldNamesToIncludeInToString.add("template");
+ }
+
+ public GenericPresetVariable getDiskOffering() {
+ return diskOffering;
+ }
+
+ public void setDiskOffering(GenericPresetVariable diskOffering) {
+ this.diskOffering = diskOffering;
+ fieldNamesToIncludeInToString.add("diskOffering");
+ }
+
+ public Storage getStorage() {
+ return storage;
+ }
+
+ public void setStorage(Storage storage) {
+ this.storage = storage;
+ fieldNamesToIncludeInToString.add("storage");
+ }
+
+ public ComputingResources getComputingResources() {
+ return computingResources;
+ }
+
+ public void setComputingResources(ComputingResources computingResources) {
+ this.computingResources = computingResources;
+ fieldNamesToIncludeInToString.add("computingResources");
+ }
+
+ public Long getVirtualSize() {
+ return virtualSize;
+ }
+
+ public void setVirtualSize(Long virtualSize) {
+ this.virtualSize = virtualSize;
+ fieldNamesToIncludeInToString.add("virtualSize");
+ }
+
+ public BackupOffering getBackupOffering() {
+ return backupOffering;
+ }
+
+ public void setBackupOffering(BackupOffering backupOffering) {
+ this.backupOffering = backupOffering;
+ fieldNamesToIncludeInToString.add("backupOffering");
+ }
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
index 31875a167da..35106654dfe 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
@@ -57,6 +57,9 @@ public interface QuotaConfig {
public static final ConfigKey QuotaSmtpUseStartTLS = new ConfigKey("Advanced", String.class, "quota.usage.smtp.useStartTLS", "false",
"If set to true and if we enable security via quota.usage.smtp.useAuth, this will enable StartTLS to secure the connection.", true);
+ public static final ConfigKey QuotaActivationRuleTimeout = new ConfigKey<>("Advanced", Long.class, "quota.activationrule.timeout", "2000", "The maximum runtime,"
+ + " in milliseconds, to execute the quota tariff's activation rule; if it is reached, a timeout will happen.", true);
+
enum QuotaEmailTemplateTypes {
QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT
}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java
index babb4edb520..653ad2a94b2 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java
@@ -21,12 +21,9 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.cloudstack.usage.UsageTypes;
+import org.apache.cloudstack.usage.UsageUnitTypes;
public class QuotaTypes extends UsageTypes {
- public static final int CPU_CLOCK_RATE = 15;
- public static final int CPU_NUMBER = 16;
- public static final int MEMORY = 17;
-
private final Integer quotaType;
private final String quotaName;
private final String quotaUnit;
@@ -36,31 +33,28 @@ public class QuotaTypes extends UsageTypes {
static {
final HashMap quotaTypeList = new HashMap();
- quotaTypeList.put(RUNNING_VM, new QuotaTypes(RUNNING_VM, "RUNNING_VM", "Compute-Month", "Running Vm Usage"));
- quotaTypeList.put(ALLOCATED_VM, new QuotaTypes(ALLOCATED_VM, "ALLOCATED_VM", "Compute-Month", "Allocated Vm Usage"));
- quotaTypeList.put(IP_ADDRESS, new QuotaTypes(IP_ADDRESS, "IP_ADDRESS", "IP-Month", "IP Address Usage"));
- quotaTypeList.put(NETWORK_BYTES_SENT, new QuotaTypes(NETWORK_BYTES_SENT, "NETWORK_BYTES_SENT", "GB", "Network Usage (Bytes Sent)"));
- quotaTypeList.put(NETWORK_BYTES_RECEIVED, new QuotaTypes(NETWORK_BYTES_RECEIVED, "NETWORK_BYTES_RECEIVED", "GB", "Network Usage (Bytes Received)"));
- quotaTypeList.put(VOLUME, new QuotaTypes(VOLUME, "VOLUME", "GB-Month", "Volume Usage"));
- quotaTypeList.put(TEMPLATE, new QuotaTypes(TEMPLATE, "TEMPLATE", "GB-Month", "Template Usage"));
- quotaTypeList.put(ISO, new QuotaTypes(ISO, "ISO", "GB-Month", "ISO Usage"));
- quotaTypeList.put(SNAPSHOT, new QuotaTypes(SNAPSHOT, "SNAPSHOT", "GB-Month", "Snapshot Usage"));
- quotaTypeList.put(SECURITY_GROUP, new QuotaTypes(SECURITY_GROUP, "SECURITY_GROUP", "Policy-Month", "Security Group Usage"));
- quotaTypeList.put(LOAD_BALANCER_POLICY, new QuotaTypes(LOAD_BALANCER_POLICY, "LOAD_BALANCER_POLICY", "Policy-Month", "Load Balancer Usage"));
- quotaTypeList.put(PORT_FORWARDING_RULE, new QuotaTypes(PORT_FORWARDING_RULE, "PORT_FORWARDING_RULE", "Policy-Month", "Port Forwarding Usage"));
- quotaTypeList.put(NETWORK_OFFERING, new QuotaTypes(NETWORK_OFFERING, "NETWORK_OFFERING", "Policy-Month", "Network Offering Usage"));
- quotaTypeList.put(VPN_USERS, new QuotaTypes(VPN_USERS, "VPN_USERS", "Policy-Month", "VPN users usage"));
- quotaTypeList.put(VM_DISK_IO_READ, new QuotaTypes(VM_DISK_IO_READ, "VM_DISK_IO_READ", "GB", "VM Disk usage(I/O Read)"));
- quotaTypeList.put(VM_DISK_IO_WRITE, new QuotaTypes(VM_DISK_IO_WRITE, "VM_DISK_IO_WRITE", "GB", "VM Disk usage(I/O Write)"));
- quotaTypeList.put(VM_DISK_BYTES_READ, new QuotaTypes(VM_DISK_BYTES_READ, "VM_DISK_BYTES_READ", "GB", "VM Disk usage(Bytes Read)"));
- quotaTypeList.put(VM_DISK_BYTES_WRITE, new QuotaTypes(VM_DISK_BYTES_WRITE, "VM_DISK_BYTES_WRITE", "GB", "VM Disk usage(Bytes Write)"));
- quotaTypeList.put(VM_SNAPSHOT, new QuotaTypes(VM_SNAPSHOT, "VM_SNAPSHOT", "GB-Month", "VM Snapshot storage usage"));
- quotaTypeList.put(VOLUME_SECONDARY, new QuotaTypes(VOLUME_SECONDARY, "VOLUME_SECONDARY", "GB-Month", "Volume secondary storage usage"));
- quotaTypeList.put(VM_SNAPSHOT_ON_PRIMARY, new QuotaTypes(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", "GB-Month", "VM Snapshot primary storage usage"));
- quotaTypeList.put(BACKUP, new QuotaTypes(BACKUP, "BACKUP", "GB-Month", "Backup storage usage"));
- quotaTypeList.put(CPU_CLOCK_RATE, new QuotaTypes(CPU_CLOCK_RATE, "CPU_CLOCK_RATE", "Compute-Month", "Quota tariff for using 1 CPU of clock rate 100MHz"));
- quotaTypeList.put(CPU_NUMBER, new QuotaTypes(CPU_NUMBER, "CPU_NUMBER", "Compute-Month", "Quota tariff for running VM that has 1vCPU"));
- quotaTypeList.put(MEMORY, new QuotaTypes(MEMORY, "MEMORY", "Compute-Month", "Quota tariff for using 1MB of RAM"));
+ quotaTypeList.put(RUNNING_VM, new QuotaTypes(RUNNING_VM, "RUNNING_VM", UsageUnitTypes.COMPUTE_MONTH.toString(), "Running Vm Usage"));
+ quotaTypeList.put(ALLOCATED_VM, new QuotaTypes(ALLOCATED_VM, "ALLOCATED_VM", UsageUnitTypes.COMPUTE_MONTH.toString(), "Allocated Vm Usage"));
+ quotaTypeList.put(IP_ADDRESS, new QuotaTypes(IP_ADDRESS, "IP_ADDRESS", UsageUnitTypes.IP_MONTH.toString(), "IP Address Usage"));
+ quotaTypeList.put(NETWORK_BYTES_SENT, new QuotaTypes(NETWORK_BYTES_SENT, "NETWORK_BYTES_SENT", UsageUnitTypes.GB.toString(), "Network Usage (Bytes Sent)"));
+ quotaTypeList.put(NETWORK_BYTES_RECEIVED, new QuotaTypes(NETWORK_BYTES_RECEIVED, "NETWORK_BYTES_RECEIVED", UsageUnitTypes.GB.toString(), "Network Usage (Bytes Received)"));
+ quotaTypeList.put(VOLUME, new QuotaTypes(VOLUME, "VOLUME", UsageUnitTypes.GB_MONTH.toString(), "Volume Usage"));
+ quotaTypeList.put(TEMPLATE, new QuotaTypes(TEMPLATE, "TEMPLATE", UsageUnitTypes.GB_MONTH.toString(), "Template Usage"));
+ quotaTypeList.put(ISO, new QuotaTypes(ISO, "ISO", UsageUnitTypes.GB_MONTH.toString(), "ISO Usage"));
+ quotaTypeList.put(SNAPSHOT, new QuotaTypes(SNAPSHOT, "SNAPSHOT", UsageUnitTypes.GB_MONTH.toString(), "Snapshot Usage"));
+ quotaTypeList.put(SECURITY_GROUP, new QuotaTypes(SECURITY_GROUP, "SECURITY_GROUP", UsageUnitTypes.POLICY_MONTH.toString(), "Security Group Usage"));
+ quotaTypeList.put(LOAD_BALANCER_POLICY, new QuotaTypes(LOAD_BALANCER_POLICY, "LOAD_BALANCER_POLICY", UsageUnitTypes.POLICY_MONTH.toString(), "Load Balancer Usage"));
+ quotaTypeList.put(PORT_FORWARDING_RULE, new QuotaTypes(PORT_FORWARDING_RULE, "PORT_FORWARDING_RULE", UsageUnitTypes.POLICY_MONTH.toString(), "Port Forwarding Usage"));
+ quotaTypeList.put(NETWORK_OFFERING, new QuotaTypes(NETWORK_OFFERING, "NETWORK_OFFERING", UsageUnitTypes.POLICY_MONTH.toString(), "Network Offering Usage"));
+ quotaTypeList.put(VPN_USERS, new QuotaTypes(VPN_USERS, "VPN_USERS", UsageUnitTypes.POLICY_MONTH.toString(), "VPN users usage"));
+ quotaTypeList.put(VM_DISK_IO_READ, new QuotaTypes(VM_DISK_IO_READ, "VM_DISK_IO_READ", UsageUnitTypes.GB.toString(), "VM Disk usage(I/O Read)"));
+ quotaTypeList.put(VM_DISK_IO_WRITE, new QuotaTypes(VM_DISK_IO_WRITE, "VM_DISK_IO_WRITE", UsageUnitTypes.GB.toString(), "VM Disk usage(I/O Write)"));
+ quotaTypeList.put(VM_DISK_BYTES_READ, new QuotaTypes(VM_DISK_BYTES_READ, "VM_DISK_BYTES_READ", UsageUnitTypes.GB.toString(), "VM Disk usage(Bytes Read)"));
+ quotaTypeList.put(VM_DISK_BYTES_WRITE, new QuotaTypes(VM_DISK_BYTES_WRITE, "VM_DISK_BYTES_WRITE", UsageUnitTypes.GB.toString(), "VM Disk usage(Bytes Write)"));
+ quotaTypeList.put(VM_SNAPSHOT, new QuotaTypes(VM_SNAPSHOT, "VM_SNAPSHOT", UsageUnitTypes.GB_MONTH.toString(), "VM Snapshot storage usage"));
+ quotaTypeList.put(VOLUME_SECONDARY, new QuotaTypes(VOLUME_SECONDARY, "VOLUME_SECONDARY", UsageUnitTypes.GB_MONTH.toString(), "Volume secondary storage usage"));
+ quotaTypeList.put(VM_SNAPSHOT_ON_PRIMARY, new QuotaTypes(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", UsageUnitTypes.GB_MONTH.toString(), "VM Snapshot primary storage usage"));
+ quotaTypeList.put(BACKUP, new QuotaTypes(BACKUP, "BACKUP", UsageUnitTypes.GB_MONTH.toString(), "Backup storage usage"));
quotaTypeMap = Collections.unmodifiableMap(quotaTypeList);
}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java
index e2ed3127c05..4f13fb33180 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDao.java
@@ -26,6 +26,10 @@ import java.util.List;
public interface QuotaTariffDao extends GenericDao {
+ Pair, Integer> listQuotaTariffs(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listAll, Long startIndex, Long pageSize);
+
+ QuotaTariffVO findByName(String name);
+
QuotaTariffVO findTariffPlanByUsageType(int quotaType, Date onOrBefore);
Pair, Integer> listAllTariffPlans();
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java
index 8b1f2c20e14..470c84ac4e1 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaTariffDaoImpl.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -124,7 +125,7 @@ public class QuotaTariffDaoImpl extends GenericDaoBase impl
if (result != null && !result.isEmpty()) {
tariffs.add(result.get(0));
if (s_logger.isDebugEnabled()) {
- s_logger.debug("ListAllTariffPlans on or before " + effectiveDate + " quota type " + result.get(0).getDescription() + " , effective Date="
+ s_logger.debug("ListAllTariffPlans on or before " + effectiveDate + " quota type " + result.get(0).getUsageTypeDescription() + " , effective Date="
+ result.get(0).getEffectiveOn() + " val=" + result.get(0).getCurrencyValue());
}
}
@@ -156,4 +157,78 @@ public class QuotaTariffDaoImpl extends GenericDaoBase impl
}
});
}
+
+ @Override
+ public Pair, Integer> listQuotaTariffs(Date startDate, Date endDate, Integer usageType, String name, String uuid, boolean listAll, Long startIndex, Long pageSize) {
+ SearchCriteria searchCriteria = createListQuotaTariffsSearchCriteria(startDate, endDate, usageType, name, uuid);
+ Filter sorter = new Filter(QuotaTariffVO.class, "usageType", false, startIndex, pageSize);
+ sorter.addOrderBy(QuotaTariffVO.class, "effectiveOn", false);
+ sorter.addOrderBy(QuotaTariffVO.class, "updatedOn", false);
+
+ return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback, Integer>>) status -> searchAndCount(searchCriteria, sorter, listAll));
+ }
+
+ protected SearchCriteria createListQuotaTariffsSearchCriteria(Date startDate, Date endDate, Integer usageType, String name, String uuid) {
+ SearchCriteria searchCriteria = createListQuotaTariffsSearchBuilder(startDate, endDate, usageType, name, uuid).create();
+
+ searchCriteria.setParametersIfNotNull("start_date", startDate);
+ searchCriteria.setParametersIfNotNull("end_date", endDate);
+ searchCriteria.setParametersIfNotNull("usage_type", usageType);
+ searchCriteria.setParametersIfNotNull("name", name);
+ searchCriteria.setParametersIfNotNull("uuid", uuid);
+
+ return searchCriteria;
+ }
+
+ protected SearchBuilder createListQuotaTariffsSearchBuilder(Date startDate, Date endDate, Integer usageType, String name, String uuid) {
+ SearchBuilder searchBuilder = createSearchBuilder();
+
+ if (startDate != null) {
+ searchBuilder.and("start_date", searchBuilder.entity().getEffectiveOn(), SearchCriteria.Op.GTEQ);
+ }
+
+ if (endDate != null) {
+ searchBuilder.and("end_date", searchBuilder.entity().getEndDate(), SearchCriteria.Op.LTEQ);
+ }
+
+ if (usageType != null) {
+ searchBuilder.and("usage_type", searchBuilder.entity().getUsageType(), SearchCriteria.Op.EQ);
+ }
+
+ if (name != null) {
+ searchBuilder.and("name", searchBuilder.entity().getName(), SearchCriteria.Op.EQ);
+ }
+
+ if (uuid != null) {
+ searchBuilder.and("uuid", searchBuilder.entity().getUuid(), SearchCriteria.Op.EQ);
+ }
+
+ return searchBuilder;
+ }
+
+ @Override
+ public QuotaTariffVO findByName(String name) {
+ Pair, Integer> pairQuotaTariffs = listQuotaTariffs(null, null, null, name, null, false, null, null);
+ List quotaTariffs = pairQuotaTariffs.first();
+
+ if (CollectionUtils.isEmpty(quotaTariffs)) {
+ s_logger.debug(String.format("Could not find quota tariff with name [%s].", name));
+ return null;
+ }
+
+ return quotaTariffs.get(0);
+ }
+
+ @Override
+ public QuotaTariffVO findByUuid(String uuid) {
+ Pair, Integer> pairQuotaTariffs = listQuotaTariffs(null, null, null, null, uuid, false, null, null);
+ List quotaTariffs = pairQuotaTariffs.first();
+
+ if (CollectionUtils.isEmpty(quotaTariffs)) {
+ s_logger.debug(String.format("Could not find quota tariff with UUID [%s].", uuid));
+ return null;
+ }
+
+ return quotaTariffs.get(0);
+ }
}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDao.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDao.java
new file mode 100644
index 00000000000..3d2ee83135f
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDao.java
@@ -0,0 +1,24 @@
+// 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.dao;
+
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface VmTemplateDao extends GenericDao {
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDaoImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDaoImpl.java
new file mode 100644
index 00000000000..1d5e8566620
--- /dev/null
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDaoImpl.java
@@ -0,0 +1,33 @@
+// 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.dao;
+
+import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
+
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.dao.VMTemplateDaoImpl;
+import com.cloud.utils.db.GenericDaoBase;
+
+/**
+ * This class was created to specifically use in {@link PresetVariableHelper}.
+ * It was not possible to inject {@link VMTemplateDaoImpl} due to its complex injection hierarchy.
+ */
+
+public class VmTemplateDaoImpl extends GenericDaoBase implements VmTemplateDao {
+
+}
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java
index 8450d09e1e5..8c8c825665c 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java
@@ -18,6 +18,9 @@ package org.apache.cloudstack.quota.vo;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.quota.constant.QuotaTypes;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
+
+import com.cloud.utils.db.GenericDao;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -30,6 +33,7 @@ import javax.persistence.TemporalType;
import java.math.BigDecimal;
import java.util.Date;
+import java.util.UUID;
@Entity
@Table(name = "quota_tariff")
@@ -67,6 +71,25 @@ public class QuotaTariffVO implements InternalIdentity {
@Column(name = "updated_by")
private Long updatedBy = null;
+ @Column(name = "uuid")
+ private String uuid = UUID.randomUUID().toString();
+
+ @Column(name = "name", nullable = false, length = 65535)
+ protected String name = null;
+
+ @Column(name = "description", length = 65535)
+ protected String description;
+
+ @Column(name = "activation_rule", length = 65535)
+ protected String activationRule;
+
+ @Column(name = GenericDao.REMOVED_COLUMN)
+ protected Date removed;
+
+ @Column(name = "end_date")
+ @Temporal(value = TemporalType.TIMESTAMP)
+ private Date endDate;
+
public QuotaTariffVO() {
}
@@ -87,6 +110,15 @@ public class QuotaTariffVO implements InternalIdentity {
}
+ public QuotaTariffVO(QuotaTariffVO that) {
+ this(that.getUsageType(), that.getUsageName(), that.getUsageUnit(), that.getUsageDiscriminator(), that.getCurrencyValue(), that.getEffectiveOn(), that.getUpdatedOn(),
+ that.getUpdatedBy());
+ this.setName(that.getName());
+ this.setDescription(that.getDescription());
+ this.setActivationRule(that.getActivationRule());
+ this.setEndDate(that.getEndDate());
+ }
+
public void setId(Long id) {
this.id = id;
}
@@ -155,7 +187,7 @@ public class QuotaTariffVO implements InternalIdentity {
this.currencyValue = currencyValue;
}
- public String getDescription() {
+ public String getUsageTypeDescription() {
return QuotaTypes.getDescription(usageType);
}
@@ -167,4 +199,68 @@ public class QuotaTariffVO implements InternalIdentity {
public long getId() {
return this.id;
}
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getActivationRule() {
+ return activationRule;
+ }
+
+ public void setActivationRule(String activationRule) {
+ this.activationRule = activationRule;
+ }
+
+ public Date getRemoved() {
+ return removed;
+ }
+
+ public void setRemoved(Date removed) {
+ this.removed = removed;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public boolean setUsageTypeData(int usageType) {
+ QuotaTypes quotaType = QuotaTypes.listQuotaTypes().get(usageType);
+
+ if (quotaType == null) {
+ return false;
+ }
+
+ this.setUsageType(usageType);
+ this.setUsageName(quotaType.getQuotaName());
+ this.setUsageUnit(quotaType.getQuotaUnit());
+ this.setUsageDiscriminator(quotaType.getDiscriminator());
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "effectiveOn", "endDate");
+ };
}
\ No newline at end of file
diff --git a/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml b/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
index 1be58942034..7704a1e8e48 100644
--- a/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
+++ b/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
@@ -17,6 +17,7 @@
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
+
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaManagerImplTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaManagerImplTest.java
index 766656493e0..9ce20c11ba4 100644
--- a/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaManagerImplTest.java
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaManagerImplTest.java
@@ -16,228 +16,508 @@
// under the License.
package org.apache.cloudstack.quota;
-import static org.mockito.ArgumentMatchers.nullable;
-
-import java.lang.reflect.Field;
import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import javax.naming.ConfigurationException;
-
-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.activationrule.presetvariables.Domain;
+import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
+import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
+import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
+import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
+import org.apache.cloudstack.quota.constant.QuotaTypes;
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.apache.cloudstack.usage.UsageUnitTypes;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.Spy;
-import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.junit.MockitoJUnitRunner;
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;
@RunWith(MockitoJUnitRunner.class)
-public class QuotaManagerImplTest extends TestCase {
+public class QuotaManagerImplTest {
@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;
+ UsageDao usageDaoMock;
- @Spy
- QuotaManagerImpl quotaManager = new QuotaManagerImpl();
+ @Mock
+ PresetVariableHelper presetVariableHelperMock;
- private void injectMockToField(Object mock, String fieldName) throws NoSuchFieldException, IllegalAccessException {
- Field f = QuotaManagerImpl.class.getDeclaredField(fieldName);
- f.setAccessible(true);
- f.set(quotaManager, mock);
- }
+ @Mock
+ QuotaUsageDao quotaUsageDaoMock;
- @Before
- public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
- // Dummy transaction stack setup
- TransactionLegacy.open("QuotaManagerImplTest");
+ @InjectMocks
+ QuotaManagerImpl quotaManagerImplSpy = Mockito.spy(QuotaManagerImpl.class);
- injectMockToField(accountDao, "_accountDao");
- injectMockToField(quotaAcc, "_quotaAcc");
- injectMockToField(usageDao, "_usageDao");
- injectMockToField(quotaTariffDao, "_quotaTariffDao");
- injectMockToField(quotaUsageDao, "_quotaUsageDao");
- injectMockToField(serviceOfferingDao, "_serviceOfferingDao");
- injectMockToField(quotaBalanceDao, "_quotaBalanceDao");
- injectMockToField(configDao, "_configDao");
- }
+ @Mock
+ AccountVO accountVoMock;
+
+ @Mock
+ Pair, Integer> pairMock;
+
+ @Mock
+ UsageVO usageVoMock;
+
+ @Mock
+ QuotaTariffDao quotaTariffDaoMock;
+
+ @Mock
+ QuotaTariffVO quotaTariffVoMock;
+
+ @Mock
+ JsInterpreter jsInterpreterMock;
+
+ @Mock
+ PresetVariables presetVariablesMock;
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@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));
- }
+ public void isLockableTestValidateAccountTypes() {
+ List lockablesAccountTypes = Arrays.asList(Account.Type.NORMAL, Account.Type.DOMAIN_ADMIN);
- @Test
- public void testCalculateQuotaUsage() {
AccountVO accountVO = new AccountVO();
- accountVO.setId(2L);
- accountVO.setDomainId(1L);
- accountVO.setType(Account.Type.NORMAL);
- List accountVOList = new ArrayList<>();
- accountVOList.add(accountVO);
- Mockito.when(accountDao.listAll()).thenReturn(accountVOList);
+ Arrays.asList(Account.Type.values()).forEach(accountType -> {
+ accountVO.setType(accountType);
- 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());
+ if (lockablesAccountTypes.contains(accountType)) {
+ Assert.assertTrue(quotaManagerImplSpy.isLockable(accountVO));
+ } else {
+ Assert.assertFalse(quotaManagerImplSpy.isLockable(accountVO));
+ }
+ });
}
@Test
- public void testAggregatePendingQuotaRecordsForAccount() {
- AccountVO accountVO = new AccountVO();
- accountVO.setId(2L);
- accountVO.setDomainId(1L);
- accountVO.setType(Account.Type.NORMAL);
+ public void getPendingUsageRecordsForQuotaAggregationTestNullListReturnNull() {
+ Mockito.doReturn(pairMock).when(usageDaoMock).listUsageRecordsPendingForQuotaAggregation(Mockito.anyLong(), Mockito.anyLong());
+ Mockito.doReturn(null).when(pairMock).first();
- 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());
+ List result = quotaManagerImplSpy.getPendingUsageRecordsForQuotaAggregation(accountVoMock);
- QuotaUsageVO quotaUsageVO = new QuotaUsageVO();
- quotaUsageVO.setAccountId(2L);
- Mockito.doReturn(quotaUsageVO).when(quotaManager).updateQuotaAllocatedVMUsage(Mockito.eq(usageVO));
-
- assertTrue(quotaManager.aggregatePendingQuotaRecordsForAccount(accountVO, new Pair, Integer>(null, 0)).size() == 0);
- assertTrue(quotaManager.aggregatePendingQuotaRecordsForAccount(accountVO, usageRecords).size() == 1);
+ Assert.assertNull(result);
}
@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);
+ public void getPendingUsageRecordsForQuotaAggregationTestEmptyListReturnNull() {
+ Mockito.doReturn(pairMock).when(usageDaoMock).listUsageRecordsPendingForQuotaAggregation(Mockito.anyLong(), Mockito.anyLong());
+ Mockito.doReturn(new ArrayList<>()).when(pairMock).first();
- QuotaTariffVO tariffVO = new QuotaTariffVO();
- tariffVO.setCurrencyValue(new BigDecimal(1));
- Mockito.when(quotaTariffDao.findTariffPlanByUsageType(nullable(Integer.class), nullable(Date.class))).thenReturn(tariffVO);
+ List result = quotaManagerImplSpy.getPendingUsageRecordsForQuotaAggregation(accountVoMock);
- QuotaUsageVO qu = quotaManager.updateQuotaNetwork(usageVO, UsageTypes.NETWORK_BYTES_SENT);
- assertTrue(qu.getQuotaUsed().compareTo(BigDecimal.ZERO) > 0);
- qu = quotaManager.updateQuotaAllocatedVMUsage(usageVO);
- assertTrue(qu.getQuotaUsed().compareTo(BigDecimal.ZERO) > 0);
- qu = quotaManager.updateQuotaDiskUsage(usageVO, UsageTypes.VOLUME);
- assertTrue(qu.getQuotaUsed().compareTo(BigDecimal.ZERO) > 0);
- qu = quotaManager.updateQuotaRaw(usageVO, UsageTypes.VPN_USERS);
- assertTrue(qu.getQuotaUsed().compareTo(BigDecimal.ZERO) > 0);
-
- Mockito.verify(quotaUsageDao, Mockito.times(4)).persistQuotaUsage(Mockito.any(QuotaUsageVO.class));
- Mockito.verify(usageDao, Mockito.times(4)).persistUsage(Mockito.any(UsageVO.class));
+ Assert.assertNull(result);
}
@Test
- public void testProcessQuotaBalanceForAccount() {
- Date now = new Date();
- AccountVO accountVO = new AccountVO();
- accountVO.setId(2L);
- accountVO.setDomainId(1L);
- accountVO.setType(Account.Type.NORMAL);
+ public void getPendingUsageRecordsForQuotaAggregationTesNotEmptyListReturnList() {
+ List expected = Arrays.asList(new UsageVO());
- QuotaUsageVO quotaUsageVO = new QuotaUsageVO();
- quotaUsageVO.setAccountId(2L);
- quotaUsageVO.setStartDate(new Date(now.getTime()));
- quotaUsageVO.setEndDate(new Date(now.getTime()));
- List quotaListForAccount = new ArrayList<>();
- quotaListForAccount.add(quotaUsageVO);
+ Mockito.doReturn(pairMock).when(usageDaoMock).listUsageRecordsPendingForQuotaAggregation(Mockito.anyLong(), Mockito.anyLong());
+ Mockito.doReturn(expected).when(pairMock).first();
- quotaManager.processQuotaBalanceForAccount(accountVO, quotaListForAccount);
- Mockito.verify(quotaAcc, Mockito.times(1)).persistQuotaAccount(Mockito.any(QuotaAccountVO.class));
- }
+ List result = quotaManagerImplSpy.getPendingUsageRecordsForQuotaAggregation(accountVoMock);
- private AccountVO accountVO = new AccountVO();
-
- @Test
- public void testAdminLockableAccount() {
- accountVO.setType(Account.Type.ADMIN);
- assertFalse(quotaManager.isLockable(accountVO));
+ Assert.assertEquals(expected, result);
}
@Test
- public void testNormalLockableAccount() {
- accountVO.setType(Account.Type.NORMAL);
- assertTrue(quotaManager.isLockable(accountVO));
+ public void getUsageValueAccordingToUsageUnitTypeTestAllTypes() {
+ Mockito.doReturn(10.0).when(usageVoMock).getRawUsage();
+ Mockito.doReturn(ByteScaleUtils.GiB).when(usageVoMock).getSize();
+ BigDecimal aggregatedQuotaTariffsValue = new BigDecimal(400);
+
+ Arrays.asList(UsageUnitTypes.values()).forEach(type -> {
+ BigDecimal result = quotaManagerImplSpy.getUsageValueAccordingToUsageUnitType(usageVoMock, aggregatedQuotaTariffsValue, type.toString());
+ Double expected = null;
+
+ switch (type) {
+ case COMPUTE_MONTH:
+ case IP_MONTH:
+ case POLICY_MONTH:
+ //The value 5.5555556 is referent to the calculation (( tariffs values / hours in month ) * raw usage ).
+ expected = 5.5555556;
+ break;
+
+ case GB:
+ //The value 0.000004 is referent to the calculation (( raw usage / gib) * tariffs values ).
+ expected = 0.000004;
+ break;
+
+ case GB_MONTH:
+ //The value 5.5555556 is referent to the calculation (( usage size / gib ) * raw usage * ( tariffs values / hours in month )).
+ expected = 5.5555556;
+ break;
+
+ default:
+ break;
+ }
+
+ Assert.assertEquals(expected, result.doubleValue(), 0);
+ });
+ }
+
+ private void mockUsageRecordAndQuotaTariffForTests(Date usageRecordStartDate, Date usageRecordEndDate, Date quotaTariffStartDate, Date quotaTariffEndDate) {
+ Mockito.doReturn(usageRecordStartDate).when(usageVoMock).getStartDate();
+ Mockito.doReturn(usageRecordEndDate).when(usageVoMock).getEndDate();
+ Mockito.doReturn(quotaTariffStartDate).when(quotaTariffVoMock).getEffectiveOn();
+ Mockito.doReturn(quotaTariffEndDate).when(quotaTariffVoMock).getEndDate();
}
@Test
- public void tesDomainAdmingLockableAccount() {
- accountVO.setType(Account.Type.DOMAIN_ADMIN);
- assertTrue(quotaManager.isLockable(accountVO));
+ public void isQuotaTariffInPeriodToBeAppliedTestQuotaTariffEndDateIsNullAndUsageRecordEndDateIsBeforeQuotaTariffStartDateReturnFalse() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(null, sdf.parse("2022-01-20 10:00:00"), sdf.parse("2022-01-20 12:00:00"), null);
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertFalse(result);
}
@Test
- public void testReadOnlyAdminLockableAccount() {
- accountVO.setType(Account.Type.READ_ONLY_ADMIN);
- assertFalse(quotaManager.isLockable(accountVO));
+ public void isQuotaTariffInPeriodToBeAppliedTestQuotaTariffEndDateIsNullAndUsageRecordEndDateIsAfterQuotaTariffStartDateReturnTrue() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(null, sdf.parse("2022-01-21 20:00:00"), sdf.parse("2022-01-18 11:30:00"), null);
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertTrue(result);
}
@Test
- public void testResourceDomainAdminLockableAccount() {
- accountVO.setType(Account.Type.RESOURCE_DOMAIN_ADMIN);
- assertFalse(quotaManager.isLockable(accountVO));
+ public void isQuotaTariffInPeriodToBeAppliedTestQuotaTariffEndDateIsNullAndUsageRecordEndDateIsEqualToQuotaTariffStartDateReturnTrue() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(null, sdf.parse("2022-01-12 00:00:00"), sdf.parse("2022-01-12 00:00:00"), null);
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertTrue(result);
}
@Test
- public void testProjectLockableAccount() {
- accountVO.setType(Account.Type.PROJECT);
- assertFalse(quotaManager.isLockable(accountVO));
+ public void isQuotaTariffInPeriodToBeAppliedTestUsageRecordStartDateIsAfterQuotaTariffEndDateAndUsageRecordEndDateIsAfterQuotaTariffStartDateReturnFalse() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(sdf.parse("2022-01-11 00:00:00"), sdf.parse("2022-01-12 00:00:00"), sdf.parse("2022-01-08 00:00:00"), sdf.parse("2022-01-10 00:00:00"));
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertFalse(result);
}
+ @Test
+ public void isQuotaTariffInPeriodToBeAppliedTestUsageRecordStartDateIsEqualToQuotaTariffEndDateAndUsageRecordEndDateIsAfterQuotaTariffStartDateReturnTrue() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(sdf.parse("2022-01-18 17:35:12"), sdf.parse("2022-01-12 00:00:00"), sdf.parse("2022-01-08 00:00:00"), sdf.parse("2022-01-18 17:35:12"));
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ public void isQuotaTariffInPeriodToBeAppliedTestUsageRecordStartDateIsBeforeQuotaTariffEndDateAndUsageRecordEndDateIsAfterQuotaTariffStartDateReturnTrue() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(sdf.parse("2022-01-15 00:23:15"), sdf.parse("2022-01-16 00:23:15"), sdf.parse("2022-01-08 00:00:00"), sdf.parse("2022-01-16 00:50:08"));
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ public void isQuotaTariffInPeriodToBeAppliedTestUsageRecordStartDateIsBeforeQuotaTariffEndDateAndUsageRecordEndDateIsBeforeQuotaTariffStartDateReturnFalse() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(sdf.parse("2022-01-20 11:44:37"), sdf.parse("2022-01-21 11:44:37"), sdf.parse("2022-01-22 15:06:32"), sdf.parse("2022-01-28 18:33:01"));
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertFalse(result);
+ }
+
+ @Test
+ public void isQuotaTariffInPeriodToBeAppliedTestUsageRecordStartDateIsBeforeQuotaTariffEndDateAndUsageRecordEndDateIsEqualToQuotaTariffStartDateReturnTrue() throws ParseException {
+ mockUsageRecordAndQuotaTariffForTests(sdf.parse("2022-01-20 11:44:37"), sdf.parse("2022-01-22 15:06:32"), sdf.parse("2022-01-22 15:06:32"), sdf.parse("2022-01-28 18:33:01"));
+ boolean result = quotaManagerImplSpy.isQuotaTariffInPeriodToBeApplied(usageVoMock, quotaTariffVoMock, "");
+
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ public void injectPresetVariablesIntoJsInterpreterTestProjectIsNullDoNotInjectProject() {
+ Mockito.doNothing().when(jsInterpreterMock).injectVariable(Mockito.anyString(), Mockito.anyString());
+
+ Mockito.doReturn(new org.apache.cloudstack.quota.activationrule.presetvariables.Account()).when(presetVariablesMock).getAccount();
+ Mockito.doReturn(new Domain()).when(presetVariablesMock).getDomain();
+ Mockito.doReturn(null).when(presetVariablesMock).getProject();
+ Mockito.doReturn("test").when(presetVariablesMock).getResourceType();
+ Mockito.doReturn(new Value()).when(presetVariablesMock).getValue();
+ Mockito.doReturn(new GenericPresetVariable()).when(presetVariablesMock).getZone();
+
+ quotaManagerImplSpy.injectPresetVariablesIntoJsInterpreter(jsInterpreterMock, presetVariablesMock);
+
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("domain"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock, Mockito.never()).injectVariable(Mockito.eq("project"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("resourceType"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString());
+ }
+
+ @Test
+ public void injectPresetVariablesIntoJsInterpreterTestProjectIsNotNullInjectProject() {
+ Mockito.doNothing().when(jsInterpreterMock).injectVariable(Mockito.anyString(), Mockito.anyString());
+
+ Mockito.doReturn(new org.apache.cloudstack.quota.activationrule.presetvariables.Account()).when(presetVariablesMock).getAccount();
+ Mockito.doReturn(new Domain()).when(presetVariablesMock).getDomain();
+ Mockito.doReturn(new GenericPresetVariable()).when(presetVariablesMock).getProject();
+ Mockito.doReturn("test").when(presetVariablesMock).getResourceType();
+ Mockito.doReturn(new Value()).when(presetVariablesMock).getValue();
+ Mockito.doReturn(new GenericPresetVariable()).when(presetVariablesMock).getZone();
+
+ quotaManagerImplSpy.injectPresetVariablesIntoJsInterpreter(jsInterpreterMock, presetVariablesMock);
+
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("domain"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("project"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("resourceType"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString());
+ Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString());
+ }
+
+ @Test
+ public void createMapQuotaTariffsPerUsageTypeTestNoTariffs() {
+ Mockito.doReturn(new Pair<>(new ArrayList<>(), 0)).when(quotaTariffDaoMock).listQuotaTariffs(Mockito.any(), Mockito.any(), Mockito.any(),Mockito.any(), Mockito.any(),
+ Mockito.anyBoolean(), Mockito.any(), Mockito.any());
+
+ Map, Boolean>> result = quotaManagerImplSpy.createMapQuotaTariffsPerUsageType();
+
+ for (Map.Entry entry : QuotaTypes.listQuotaTypes().entrySet()) {
+ Pair, Boolean> pair = result.get(entry.getKey());
+ Assert.assertTrue(pair.first().isEmpty());
+ Assert.assertFalse(pair.second());
+ }
+ }
+
+ @Test
+ public void createMapQuotaTariffsPerUsageTypeTestTariffsWithEmptyActivationRule() {
+ List tariffs = new ArrayList<>();
+ QuotaTariffVO tariff = new QuotaTariffVO(1);
+ tariff.setActivationRule("");
+ tariffs.add(tariff);
+
+ Mockito.doReturn(new Pair<>(tariffs, tariffs.size())).when(quotaTariffDaoMock).listQuotaTariffs(Mockito.any(), Mockito.any(), Mockito.any(),Mockito.any(), Mockito.any(),
+ Mockito.anyBoolean(), Mockito.any(), Mockito.any());
+
+ Map, Boolean>> result = quotaManagerImplSpy.createMapQuotaTariffsPerUsageType();
+
+ for (Map.Entry entry : QuotaTypes.listQuotaTypes().entrySet()) {
+ Pair, Boolean> pair = result.get(entry.getKey());
+ if (entry.getKey() == 1) {
+ Assert.assertFalse(pair.first().isEmpty());
+ } else {
+ Assert.assertTrue(pair.first().isEmpty());
+ }
+ Assert.assertFalse(pair.second());
+ }
+ }
+
+ @Test
+ public void createMapQuotaTariffsPerUsageTypeTestTariffsWithActivationRule() {
+ List tariffs = new ArrayList<>();
+ QuotaTariffVO tariff = new QuotaTariffVO(1);
+ tariff.setActivationRule(" ");
+ tariffs.add(tariff);
+
+ Mockito.doReturn(new Pair<>(tariffs, tariffs.size())).when(quotaTariffDaoMock).listQuotaTariffs(Mockito.any(), Mockito.any(), Mockito.any(),Mockito.any(), Mockito.any(),
+ Mockito.anyBoolean(), Mockito.any(), Mockito.any());
+
+ Map, Boolean>> result = quotaManagerImplSpy.createMapQuotaTariffsPerUsageType();
+
+ for (Map.Entry entry : QuotaTypes.listQuotaTypes().entrySet()) {
+ Pair, Boolean> pair = result.get(entry.getKey());
+ if (entry.getKey() == 1) {
+ Assert.assertFalse(pair.first().isEmpty());
+ Assert.assertTrue(pair.second());
+ } else {
+ Assert.assertTrue(pair.first().isEmpty());
+ Assert.assertFalse(pair.second());
+ }
+ }
+ }
+
+ @Test
+ public void createQuotaUsageAccordingToUsageUnitTariffValueZeroReturnNull() {
+ QuotaUsageVO result = quotaManagerImplSpy.createQuotaUsageAccordingToUsageUnit(usageVoMock, BigDecimal.ZERO, null);
+ Assert.assertNull(result);
+ }
+
+ @Test
+ public void createQuotaUsageAccordingToUsageUnitTariffValueIsNotZeroReturnObject() {
+ Date startDate = new Date();
+ Date endDate = new Date();
+
+ QuotaTypes.listQuotaTypes().entrySet().forEach(entry -> {
+ Mockito.doReturn(entry.getKey()).when(usageVoMock).getUsageType();
+ Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getUsageValueAccordingToUsageUnitType(Mockito.any(), Mockito.any(), Mockito.anyString());
+ Mockito.doReturn(2l).when(usageVoMock).getId();
+ Mockito.doReturn(3l).when(usageVoMock).getZoneId();
+ Mockito.doReturn(4l).when(usageVoMock).getAccountId();
+ Mockito.doReturn(5l).when(usageVoMock).getDomainId();
+ Mockito.doReturn(startDate).when(usageVoMock).getStartDate();
+ Mockito.doReturn(endDate).when(usageVoMock).getEndDate();
+
+ QuotaUsageVO result = quotaManagerImplSpy.createQuotaUsageAccordingToUsageUnit(usageVoMock, BigDecimal.ONE, null);
+
+ Assert.assertEquals(2l, result.getUsageItemId().longValue());
+ Assert.assertEquals(3l, result.getZoneId().longValue());
+ Assert.assertEquals(4l, result.getAccountId().longValue());
+ Assert.assertEquals(5l, result.getDomainId().longValue());
+ Assert.assertEquals(entry.getKey().intValue(), result.getUsageType());
+ Assert.assertEquals(BigDecimal.TEN, result.getQuotaUsed());
+ Assert.assertEquals(startDate, result.getStartDate());
+ Assert.assertEquals(endDate, result.getEndDate());
+ });
+ }
+
+ @Test
+ public void getQuotaTariffValueToBeAppliedTestActivationRuleIsNullReturnTariffValue() {
+ Mockito.doReturn(null).when(quotaTariffVoMock).getActivationRule();
+ Mockito.doReturn(BigDecimal.ONE).when(quotaTariffVoMock).getCurrencyValue();
+
+ BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null);
+
+ Assert.assertEquals(BigDecimal.ONE, result);
+ }
+
+ @Test
+ public void getQuotaTariffValueToBeAppliedTestActivationRuleIsEmptyReturnTariffValue() {
+ Mockito.doReturn("").when(quotaTariffVoMock).getActivationRule();
+ Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
+
+ BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null);
+
+ Assert.assertEquals(BigDecimal.TEN, result);
+ }
+
+ @Test
+ public void getQuotaTariffValueToBeAppliedTestScriptResultIsNumberReturnIt() {
+ BigDecimal expected = new BigDecimal(50.1);
+
+ Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
+ Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
+ Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
+ Mockito.doReturn(expected).when(jsInterpreterMock).executeScript(Mockito.anyString());
+
+ BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getQuotaTariffValueToBeAppliedTestScriptResultIsTrueReturnTariffValue() {
+ BigDecimal expected = new BigDecimal(236.84);
+
+ Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
+ Mockito.doReturn(expected).when(quotaTariffVoMock).getCurrencyValue();
+ Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
+ Mockito.doReturn(true).when(jsInterpreterMock).executeScript(Mockito.anyString());
+
+ BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getQuotaTariffValueToBeAppliedTestScriptResultIsFalseReturnZero() {
+ Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
+ Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
+ Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
+ Mockito.doReturn(false).when(jsInterpreterMock).executeScript(Mockito.anyString());
+
+ BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
+
+ Assert.assertEquals(BigDecimal.ZERO, result);
+ }
+
+ @Test
+ public void getQuotaTariffValueToBeAppliedTestScriptResultIsNotBooleanNorNumericReturnZero() {
+ Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
+ Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
+ Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
+ Mockito.doReturn("test").when(jsInterpreterMock).executeScript(Mockito.anyString());
+
+ BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
+
+ Assert.assertEquals(BigDecimal.ZERO, result);
+ }
+
+ @Test
+ public void getPresetVariablesTestDoesNotHaveTariffsWithActivationRuleReturnNull() {
+ PresetVariables result = quotaManagerImplSpy.getPresetVariables(false, usageVoMock);
+ Assert.assertNull(result);
+ }
+
+ @Test
+ public void getPresetVariablesTestHasTariffsWithActivationRuleReturnPresetVariables() {
+ Mockito.doReturn(presetVariablesMock).when(presetVariableHelperMock).getPresetVariables(Mockito.any());
+ PresetVariables result = quotaManagerImplSpy.getPresetVariables(true, usageVoMock);
+ Assert.assertEquals(presetVariablesMock, result);
+ }
+
+ @Test
+ public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnZero() {
+ List tariffs = new ArrayList<>();
+ tariffs.add(new QuotaTariffVO());
+ tariffs.add(new QuotaTariffVO());
+ tariffs.add(new QuotaTariffVO());
+
+ Mockito.doReturn(false).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
+ BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
+
+ Assert.assertEquals(BigDecimal.ZERO, result);
+ }
+
+ @Test
+ public void aggregateQuotaTariffsValuesTestTariffsIsEmptyReturnZero() {
+ BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, new ArrayList<>(), false, jsInterpreterMock, "");
+
+ Assert.assertEquals(BigDecimal.ZERO, result);
+ }
+
+ @Test
+ public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnAggregation() {
+ List tariffs = new ArrayList<>();
+ tariffs.add(new QuotaTariffVO());
+ tariffs.add(new QuotaTariffVO());
+ tariffs.add(new QuotaTariffVO());
+
+ Mockito.doReturn(true, false, true).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
+ Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any());
+ BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
+
+ Assert.assertEquals(BigDecimal.TEN.multiply(new BigDecimal(2)), result);
+ }
+
+ @Test
+ public void persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsagesTestReturnOnlyPersistedQuotaUsageVo() {
+ List> listPair = new ArrayList<>();
+ QuotaUsageVO quotaUsageVoMock1 = Mockito.mock(QuotaUsageVO.class);
+ QuotaUsageVO quotaUsageVoMock2 = Mockito.mock(QuotaUsageVO.class);
+
+ listPair.add(new Pair<>(new UsageVO(), quotaUsageVoMock1));
+ listPair.add(new Pair<>(new UsageVO(), null));
+ listPair.add(new Pair<>(new UsageVO(), quotaUsageVoMock2));
+
+ Mockito.doReturn(null).when(usageDaoMock).persistUsage(Mockito.any());
+ Mockito.doReturn(null).when(quotaUsageDaoMock).persistQuotaUsage(Mockito.any());
+
+ List result = quotaManagerImplSpy.persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(listPair);
+
+ Assert.assertEquals(2, result.size());
+ Assert.assertEquals(quotaUsageVoMock1, result.get(0));
+ Assert.assertEquals(quotaUsageVoMock2, result.get(1));
+ }
}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java
new file mode 100644
index 00000000000..1e62235e71c
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java
@@ -0,0 +1,34 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AccountTest {
+
+ @Test
+ public void setRoleTestAddFieldRoleToCollection() {
+ Account variable = new Account();
+ variable.setRole(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("role"));
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOfferingTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOfferingTest.java
new file mode 100644
index 00000000000..57c18f936f2
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOfferingTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class BackupOfferingTest {
+ @Test
+ public void setExternalIdTestAddFieldExternalIdToCollection() {
+ BackupOffering backupOffering = new BackupOffering();
+ backupOffering.setExternalId("any-external-id");
+ Assert.assertTrue(backupOffering.fieldNamesToIncludeInToString.contains("externalId"));
+ }
+
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOfferingTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOfferingTest.java
new file mode 100644
index 00000000000..5fbcbe76476
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOfferingTest.java
@@ -0,0 +1,35 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ComputeOfferingTest {
+
+ @Test
+ public void setCustomizedTestAddFieldCustomizedToCollection() {
+ ComputeOffering variable = new ComputeOffering();
+ variable.setCustomized(true);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("customized"));
+ }
+
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResourcesTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResourcesTest.java
new file mode 100644
index 00000000000..f7978f16e04
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResourcesTest.java
@@ -0,0 +1,40 @@
+// 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.activationrule.presetvariables;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ComputingResourcesTest {
+
+ @Test
+ public void toStringTestReturnAJson() {
+ ComputingResources variable = new ComputingResources();
+
+ String expected = ToStringBuilder.reflectionToString(variable, ToStringStyle.JSON_STYLE);
+ String result = variable.toString();
+
+ Assert.assertEquals(expected, result);
+ }
+
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/DomainTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/DomainTest.java
new file mode 100644
index 00000000000..f245b4637e6
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/DomainTest.java
@@ -0,0 +1,35 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DomainTest {
+
+ @Test
+ public void setPathTestAddFieldPathToCollection() {
+ Domain variable = new Domain();
+ variable.setPath("test path");
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("path"));
+ }
+
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariableTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariableTest.java
new file mode 100644
index 00000000000..4f594ee5d00
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariableTest.java
@@ -0,0 +1,73 @@
+// 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.activationrule.presetvariables;
+
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GenericPresetVariableTest {
+
+ @Test
+ public void setIdTestAddFieldIdToCollection() {
+ GenericPresetVariable variable = new GenericPresetVariable();
+ variable.setId("test");
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("id"));
+ }
+
+ @Test
+ public void setNameTestAddFieldNameToCollection() {
+ GenericPresetVariable variable = new GenericPresetVariable();
+ variable.setName("test");
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("name"));
+ }
+
+ @Test
+ public void toStringTestSetAllFieldsAndReturnAJson() {
+ GenericPresetVariable variable = new GenericPresetVariable();
+ variable.setId("test id");
+ variable.setName("test name");
+
+ String expected = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(variable, "id", "name");
+ String result = variable.toString();
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void toStringTestSetSomeFieldsAndReturnAJson() {
+ GenericPresetVariable variable = new GenericPresetVariable();
+ variable.setId("test id");
+
+ String expected = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(variable, "id");
+ String result = variable.toString();
+
+ Assert.assertEquals(expected, result);
+
+ variable = new GenericPresetVariable();
+ variable.setName("test name");
+
+ expected = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(variable, "name");
+ result = variable.toString();
+
+ Assert.assertEquals(expected, result);
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java
new file mode 100644
index 00000000000..87aae7788e2
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java
@@ -0,0 +1,34 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class HostTest {
+
+ @Test
+ public void setTagsTestAddFieldTagsToCollection() {
+ Host variable = new Host();
+ variable.setTags(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("tags"));
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelperTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelperTest.java
new file mode 100644
index 00000000000..d195b4ffc99
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelperTest.java
@@ -0,0 +1,1151 @@
+// 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.activationrule.presetvariables;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.acl.RoleVO;
+import org.apache.cloudstack.acl.dao.RoleDao;
+import org.apache.cloudstack.backup.BackupOfferingVO;
+import org.apache.cloudstack.backup.dao.BackupOfferingDao;
+import org.apache.cloudstack.quota.constant.QuotaTypes;
+import org.apache.cloudstack.quota.dao.VmTemplateDao;
+import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
+import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.usage.UsageTypes;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.host.dao.HostTagsDao;
+import com.cloud.offerings.NetworkOfferingVO;
+import com.cloud.offerings.dao.NetworkOfferingDao;
+import com.cloud.server.ResourceTag;
+import com.cloud.server.ResourceTag.ResourceObjectType;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.ScopeType;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.Storage.ProvisioningType;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.dao.StoragePoolTagsDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.tags.ResourceTagVO;
+import com.cloud.tags.dao.ResourceTagDao;
+import com.cloud.usage.UsageVO;
+import com.cloud.usage.dao.UsageDao;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmDetailVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.UserVmDetailsDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.VMSnapshot;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PresetVariableHelperTest {
+
+ @Mock
+ AccountDao accountDaoMock;
+
+ @Mock
+ DataCenterDao dataCenterDaoMock;
+
+ @Mock
+ DiskOfferingDao diskOfferingDaoMock;
+
+ @Mock
+ DomainDao domainDaoMock;
+
+ @Mock
+ GuestOSDao guestOsDaoMock;
+
+ @Mock
+ HostDao hostDaoMock;
+
+ @Mock
+ HostTagsDao hostTagsDaoMock;
+
+ @Mock
+ ImageStoreDao imageStoreDaoMock;
+
+ @Mock
+ NetworkOfferingDao networkOfferingDaoMock;
+
+ @Mock
+ PrimaryDataStoreDao primaryStorageDaoMock;
+
+ @Mock
+ ResourceTagDao resourceTagDaoMock;
+
+ @Mock
+ RoleDao roleDaoMock;
+
+ @Mock
+ ServiceOfferingDao serviceOfferingDaoMock;
+
+ @Mock
+ SnapshotDao snapshotDaoMock;
+
+ @Mock
+ SnapshotDataStoreDao snapshotDataStoreDaoMock;
+
+ @Mock
+ StoragePoolTagsDao storagePoolTagsDaoMock;
+
+ @Mock
+ UsageDao usageDaoMock;
+
+ @Mock
+ VMInstanceDao vmInstanceDaoMock;
+
+ @Mock
+ VMSnapshotDao vmSnapshotDaoMock;
+
+ @Mock
+ VmTemplateDao vmTemplateDaoMock;
+
+ @Mock
+ VolumeDao volumeDaoMock;
+
+ @Mock
+ UserVmDetailsDao userVmDetailsDaoMock;
+
+ @InjectMocks
+ PresetVariableHelper presetVariableHelperSpy = Mockito.spy(PresetVariableHelper.class);
+
+ @Mock
+ UsageVO usageVoMock;
+
+ @Mock
+ VMInstanceVO vmInstanceVoMock;
+
+ @Mock
+ ServiceOfferingVO serviceOfferingVoMock;
+
+ @Mock
+ BackupOfferingDao backupOfferingDaoMock;
+
+ List runningAndAllocatedVmUsageTypes = Arrays.asList(UsageTypes.RUNNING_VM, UsageTypes.ALLOCATED_VM);
+ List templateAndIsoUsageTypes = Arrays.asList(UsageTypes.TEMPLATE, UsageTypes.ISO);
+
+ private Account getAccountForTests() {
+ Account account = new Account();
+ account.setId("account_id");
+ account.setName("account_name");
+ return account;
+ }
+
+ private Domain getDomainForTests() {
+ Domain domain = new Domain();
+ domain.setId("domain_id");
+ domain.setName("domain_name");
+ domain.setPath("domain_path");
+ return domain;
+ }
+
+ private Value getValueForTests() {
+ Value value = new Value();
+ value.setId("value_id");
+ value.setName("value_name");
+ value.setOsName("value_os_name");
+ value.setComputeOffering(getComputeOfferingForTests());
+ value.setTags(Collections.singletonMap("tag1", "value1"));
+ value.setTemplate(getGenericPresetVariableForTests());
+ value.setDiskOffering(getGenericPresetVariableForTests());
+ value.setProvisioningType(ProvisioningType.THIN);
+ value.setStorage(getStorageForTests());
+ value.setSize(ByteScaleUtils.GiB);
+ value.setSnapshotType(Snapshot.Type.HOURLY);
+ value.setTag("tag_test");
+ value.setVmSnapshotType(VMSnapshot.Type.Disk);
+ value.setComputingResources(getComputingResourcesForTests());
+ return value;
+ }
+
+ private ComputingResources getComputingResourcesForTests() {
+ ComputingResources computingResources = new ComputingResources();
+ computingResources.setCpuNumber(1);
+ computingResources.setCpuSpeed(1000);
+ computingResources.setMemory(512);
+ return computingResources;
+ }
+
+ private ComputeOffering getComputeOfferingForTests() {
+ ComputeOffering computeOffering = new ComputeOffering();
+ computeOffering.setId("compute_offering_id");
+ computeOffering.setName("compute_offering_name");
+ computeOffering.setCustomized(false);
+ return computeOffering;
+ }
+
+ private Host getHostForTests() {
+ Host host = new Host();
+ host.setId("host_id");
+ host.setName("host_name");
+ host.setTags(Arrays.asList("tag1", "tag2"));
+ return host;
+ }
+
+ private Storage getStorageForTests() {
+ Storage storage = new Storage();
+ storage.setId("storage_id");
+ storage.setName("storage_name");
+ storage.setTags(Arrays.asList("tag1", "tag2"));
+ storage.setScope(ScopeType.ZONE);
+ return storage;
+ }
+
+ private Set> getQuotaTypesForTests(Integer... typesToRemove) {
+ Map quotaTypesMap = new LinkedHashMap<>(QuotaTypes.listQuotaTypes());
+
+ if (ArrayUtils.isNotEmpty(typesToRemove)) {
+ for (Integer type : typesToRemove) {
+ quotaTypesMap.remove(type);
+ }
+ }
+
+ return quotaTypesMap.entrySet();
+ }
+
+ private List getVmDetailsForTests() {
+ List details = new LinkedList<>();
+ details.add(new UserVmDetailVO(1l, "test_with_value", "277", false));
+ details.add(new UserVmDetailVO(1l, "test_with_invalid_value", "invalid", false));
+ details.add(new UserVmDetailVO(1l, "test_with_null", null, false));
+ return details;
+ }
+
+ private void assertPresetVariableIdAndName(GenericPresetVariable expected, GenericPresetVariable result) {
+ Assert.assertEquals(expected.getId(), result.getId());
+ Assert.assertEquals(expected.getName(), result.getName());
+ }
+
+ private void validateFieldNamesToIncludeInToString(List expected, GenericPresetVariable resultObject) {
+ List result = new ArrayList<>(resultObject.fieldNamesToIncludeInToString);
+ Collections.sort(expected);
+ Collections.sort(result);
+ Assert.assertEquals(expected, result);
+ }
+
+ private BackupOffering getBackupOfferingForTests() {
+ BackupOffering backupOffering = new BackupOffering();
+ backupOffering.setId("backup_offering_id");
+ backupOffering.setName("backup_offering_name");
+ backupOffering.setExternalId("backup_offering_external_id");
+ return backupOffering;
+ }
+
+ private void mockMethodValidateIfObjectIsNull() {
+ Mockito.doNothing().when(presetVariableHelperSpy).validateIfObjectIsNull(Mockito.any(), Mockito.anyLong(), Mockito.anyString());
+ }
+
+ private GenericPresetVariable getGenericPresetVariableForTests() {
+ GenericPresetVariable gpv = new GenericPresetVariable();
+ gpv.setId("test_id");
+ gpv.setName("test_name");
+ return gpv;
+ }
+
+ @Test
+ public void getPresetVariablesTestSetFieldsAndReturnObject() {
+ PresetVariables expected = new PresetVariables();
+ expected.setAccount(getAccountForTests());
+ expected.setDomain(getDomainForTests());
+ expected.setValue(new Value());
+ expected.setZone(getGenericPresetVariableForTests());
+
+ Mockito.doReturn(expected.getAccount()).when(presetVariableHelperSpy).getPresetVariableAccount(Mockito.anyLong());
+ Mockito.doNothing().when(presetVariableHelperSpy).setPresetVariableProject(Mockito.any());
+ Mockito.doReturn(expected.getDomain()).when(presetVariableHelperSpy).getPresetVariableDomain(Mockito.anyLong());
+ Mockito.doReturn(expected.getValue()).when(presetVariableHelperSpy).getPresetVariableValue(Mockito.any(UsageVO.class));
+ Mockito.doReturn(expected.getZone()).when(presetVariableHelperSpy).getPresetVariableZone(Mockito.anyLong());
+
+ PresetVariables result = presetVariableHelperSpy.getPresetVariables(usageVoMock);
+
+ Assert.assertEquals(expected.getAccount(), result.getAccount());
+ Assert.assertEquals(expected.getDomain(), result.getDomain());
+ Assert.assertEquals(expected.getValue(), result.getValue());
+ Assert.assertEquals(expected.getZone(), result.getZone());
+ }
+
+ @Test
+ public void setPresetVariableProjectTestAccountWithRoleDoNotSetAsProject() {
+ PresetVariables result = new PresetVariables();
+ result.setAccount(new Account());
+ result.getAccount().setRole(new Role());
+
+ presetVariableHelperSpy.setPresetVariableProject(result);
+
+ Assert.assertNull(result.getProject());
+ }
+
+ @Test
+ public void setPresetVariableProjectTestAccountWithoutRoleSetAsProject() {
+ PresetVariables result = new PresetVariables();
+ Account account = getAccountForTests();
+ result.setAccount(account);
+
+ presetVariableHelperSpy.setPresetVariableProject(result);
+
+ Assert.assertNotNull(result.getProject());
+ assertPresetVariableIdAndName(account, result.getProject());
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name"), result.getProject());
+ }
+
+ @Test
+ public void getPresetVariableAccountTestSetValuesAndReturnObject() {
+ AccountVO accountVoMock = Mockito.mock(AccountVO.class);
+ Mockito.doReturn(accountVoMock).when(accountDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+ Mockito.doNothing().when(presetVariableHelperSpy).setPresetVariableRoleInAccountIfAccountIsNotAProject(Mockito.any(), Mockito.anyLong(), Mockito.any(Account.class));
+
+ Account account = getAccountForTests();
+ Mockito.doReturn(account.getId()).when(accountVoMock).getUuid();
+ Mockito.doReturn(account.getName()).when(accountVoMock).getName();
+
+ Account result = presetVariableHelperSpy.getPresetVariableAccount(1l);
+
+ assertPresetVariableIdAndName(account, result);
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name"), result);
+ }
+
+ @Test
+ public void setPresetVariableRoleInAccountIfAccountIsNotAProjectTestAllCases() {
+ Mockito.doReturn(new Role()).when(presetVariableHelperSpy).getPresetVariableRole(Mockito.anyLong());
+
+
+ for (com.cloud.user.Account.Type type : com.cloud.user.Account.Type.values()) {
+ Account account = new Account();
+ presetVariableHelperSpy.setPresetVariableRoleInAccountIfAccountIsNotAProject(type, 1L, account);
+
+ if (com.cloud.user.Account.Type.PROJECT == type) {
+ Assert.assertNull(account.getRole());
+ } else {
+ Assert.assertNotNull(account.getRole());
+ }
+ }
+ }
+
+ @Test
+ public void getPresetVariableRoleTestSetValuesAndReturnObject() {
+ RoleVO roleVoMock = Mockito.mock(RoleVO.class);
+ Mockito.doReturn(roleVoMock).when(roleDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ Arrays.asList(RoleType.values()).forEach(roleType -> {
+ Role role = new Role();
+ role.setId("test_id");
+ role.setName("test_name");
+ role.setType(roleType);
+
+ Mockito.doReturn(role.getId()).when(roleVoMock).getUuid();
+ Mockito.doReturn(role.getName()).when(roleVoMock).getName();
+ Mockito.doReturn(role.getType()).when(roleVoMock).getRoleType();
+
+ Role result = presetVariableHelperSpy.getPresetVariableRole(1l);
+
+ assertPresetVariableIdAndName(role, result);
+ Assert.assertEquals(role.getType(), result.getType());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "type"), result);
+ });
+ }
+
+ @Test
+ public void getPresetVariableDomainTestSetValuesAndReturnObject() {
+ DomainVO domainVoMock = Mockito.mock(DomainVO.class);
+ Mockito.doReturn(domainVoMock).when(domainDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ Domain domain = getDomainForTests();
+ Mockito.doReturn(domain.getId()).when(domainVoMock).getUuid();
+ Mockito.doReturn(domain.getName()).when(domainVoMock).getName();
+ Mockito.doReturn(domain.getPath()).when(domainVoMock).getPath();
+
+ Domain result = presetVariableHelperSpy.getPresetVariableDomain(1l);
+
+ assertPresetVariableIdAndName(domain, result);
+ Assert.assertEquals(domain.getPath(), result.getPath());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "path"), result);
+ }
+
+ @Test
+ public void getPresetVariableZoneTestSetValuesAndReturnObject() {
+ DataCenterVO dataCenterVoMock = Mockito.mock(DataCenterVO.class);
+ Mockito.doReturn(dataCenterVoMock).when(dataCenterDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ GenericPresetVariable expected = getGenericPresetVariableForTests();
+ Mockito.doReturn(expected.getId()).when(dataCenterVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(dataCenterVoMock).getName();
+
+ GenericPresetVariable result = presetVariableHelperSpy.getPresetVariableZone(1l);
+
+ assertPresetVariableIdAndName(expected, result);
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name"), result);
+ }
+
+ @Test
+ public void getPresetVariableValueTestSetFieldsAndReturnObject() {
+ List resources = Arrays.asList(new Resource(), new Resource());
+
+ Mockito.doReturn(resources).when(presetVariableHelperSpy).getPresetVariableAccountResources(Mockito.any(UsageVO.class), Mockito.anyLong(), Mockito.anyInt());
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForRunningAndAllocatedVm(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForVolume(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForTemplateAndIso(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForSnapshot(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForNetworkOffering(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForVmSnapshot(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+ Mockito.doNothing().when(presetVariableHelperSpy).loadPresetVariableValueForBackup(Mockito.any(UsageVO.class), Mockito.any(Value.class));
+
+ Value result = presetVariableHelperSpy.getPresetVariableValue(usageVoMock);
+
+ Assert.assertEquals(resources, result.getAccountResources());
+ validateFieldNamesToIncludeInToString(Arrays.asList("accountResources"), result);
+ }
+
+ @Test
+ public void getPresetVariableAccountResourcesTestSetFieldsAndReturnObject() {
+ List> expected = Arrays.asList(new Pair<>("zoneId1", "domainId2"), new Pair<>("zoneId3", "domainId4"));
+ Mockito.doReturn(new Date()).when(usageVoMock).getStartDate();
+ Mockito.doReturn(new Date()).when(usageVoMock).getEndDate();
+ Mockito.doReturn(expected).when(usageDaoMock).listAccountResourcesInThePeriod(Mockito.anyLong(), Mockito.anyInt(), Mockito.any(Date.class), Mockito.any(Date.class));
+
+ List result = presetVariableHelperSpy.getPresetVariableAccountResources(usageVoMock, 1l, 0);
+
+ for (int i = 0; i < expected.size(); i++) {
+ Assert.assertEquals(expected.get(i).first(), result.get(i).getZoneId());
+ Assert.assertEquals(expected.get(i).second(), result.get(i).getDomainId());
+ }
+ }
+
+ @Test
+ public void loadPresetVariableValueForRunningAndAllocatedVmTestRecordIsNotARunningNorAnAllocatedVmDoNothing() {
+ getQuotaTypesForTests(runningAndAllocatedVmUsageTypes.toArray(new Integer[0])).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForRunningAndAllocatedVm(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(vmInstanceDaoMock);
+ }
+
+ @Test
+ public void loadPresetVariableValueForRunningAndAllocatedVmTestRecordIsRunningOrAllocatedVmSetFields() {
+ Value expected = getValueForTests();
+
+ Mockito.doReturn(vmInstanceVoMock).when(vmInstanceDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doNothing().when(presetVariableHelperSpy).setPresetVariableHostInValueIfUsageTypeIsRunningVm(Mockito.any(Value.class), Mockito.anyInt(),
+ Mockito.any(VMInstanceVO.class));
+
+ Mockito.doReturn(expected.getId()).when(vmInstanceVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(vmInstanceVoMock).getHostName();
+ Mockito.doReturn(expected.getOsName()).when(presetVariableHelperSpy).getPresetVariableValueOsName(Mockito.anyLong());
+ Mockito.doNothing().when(presetVariableHelperSpy).setPresetVariableValueServiceOfferingAndComputingResources(Mockito.any(), Mockito.anyInt(), Mockito.any());
+ Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+ Mockito.doReturn(expected.getTemplate()).when(presetVariableHelperSpy).getPresetVariableValueTemplate(Mockito.anyLong());
+
+ runningAndAllocatedVmUsageTypes.forEach(type -> {
+ Mockito.doReturn(type).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForRunningAndAllocatedVm(usageVoMock, result);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getOsName(), result.getOsName());
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ Assert.assertEquals(expected.getTemplate(), result.getTemplate());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "osName", "tags", "template"), result);
+ });
+
+ Mockito.verify(presetVariableHelperSpy, Mockito.times(runningAndAllocatedVmUsageTypes.size())).getPresetVariableValueResourceTags(Mockito.anyLong(),
+ Mockito.eq(ResourceObjectType.UserVm));
+ }
+
+ @Test
+ public void setPresetVariableHostInValueIfUsageTypeIsRunningVmTestQuotaTypeDifferentFromRunningVmDoNothing() {
+ getQuotaTypesForTests(UsageTypes.RUNNING_VM).forEach(type -> {
+ Value result = new Value();
+
+ presetVariableHelperSpy.setPresetVariableHostInValueIfUsageTypeIsRunningVm(result, type.getKey(), vmInstanceVoMock);
+
+ Assert.assertNull(result.getHost());
+ });
+ }
+
+ @Test
+ public void setPresetVariableHostInValueIfUsageTypeIsRunningVmTestQuotaTypeIsRunningVmSetHost() {
+ Value result = new Value();
+ Host expected = getHostForTests();
+
+ Mockito.doReturn(expected).when(presetVariableHelperSpy).getPresetVariableValueHost(Mockito.anyLong());
+ presetVariableHelperSpy.setPresetVariableHostInValueIfUsageTypeIsRunningVm(result, UsageTypes.RUNNING_VM, vmInstanceVoMock);
+
+ Assert.assertNotNull(result.getHost());
+
+ assertPresetVariableIdAndName(expected, result.getHost());
+ Assert.assertEquals(expected.getTags(), result.getHost().getTags());
+ validateFieldNamesToIncludeInToString(Arrays.asList("host"), result);
+ }
+
+ @Test
+ public void getPresetVariableValueHostTestSetFieldsAndReturnObject() {
+ Host expected = getHostForTests();
+ HostVO hostVoMock = Mockito.mock(HostVO.class);
+
+ Mockito.doReturn(hostVoMock).when(hostDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+ Mockito.doReturn(expected.getId()).when(hostVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(hostVoMock).getName();
+ Mockito.doReturn(expected.getTags()).when(hostTagsDaoMock).getHostTags(Mockito.anyLong());
+
+ Host result = presetVariableHelperSpy.getPresetVariableValueHost(1l);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "tags"), result);
+ }
+
+ @Test
+ public void getPresetVariableValueOsNameTestReturnDisplayName() {
+ GuestOSVO guestOsVoMock = Mockito.mock(GuestOSVO.class);
+ Mockito.doReturn(guestOsVoMock).when(guestOsDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ String expected = "os_display_name";
+ Mockito.doReturn(expected).when(guestOsVoMock).getDisplayName();
+
+ String result = presetVariableHelperSpy.getPresetVariableValueOsName(1l);
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getPresetVariableValueComputeOfferingTestSetFieldsAndReturnObject() {
+ ComputeOffering expected = getComputeOfferingForTests();
+ Mockito.doReturn(expected.getId()).when(serviceOfferingVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(serviceOfferingVoMock).getName();
+ Mockito.doReturn(expected.isCustomized()).when(serviceOfferingVoMock).isDynamic();
+
+ ComputeOffering result = presetVariableHelperSpy.getPresetVariableValueComputeOffering(serviceOfferingVoMock);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.isCustomized(), result.isCustomized());
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "customized"), result);
+ }
+
+ @Test
+ public void getPresetVariableValueTemplateTestSetValuesAndReturnObject() {
+ VMTemplateVO vmTemplateVoMock = Mockito.mock(VMTemplateVO.class);
+ Mockito.doReturn(vmTemplateVoMock).when(vmTemplateDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ GenericPresetVariable expected = getGenericPresetVariableForTests();
+ Mockito.doReturn(expected.getId()).when(vmTemplateVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(vmTemplateVoMock).getName();
+
+ GenericPresetVariable result = presetVariableHelperSpy.getPresetVariableValueTemplate(1l);
+
+ assertPresetVariableIdAndName(expected, result);
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name"), result);
+ }
+
+ @Test
+ public void getPresetVariableValueResourceTagsTestAllCases() {
+ List listExpected = new ArrayList<>();
+ listExpected.add(new ResourceTagVO("tag1", "value2", 0, 0, 0, null, null, null));
+ listExpected.add(new ResourceTagVO("tag3", "value4", 0, 0, 0, null, null, null));
+
+ Mockito.doReturn(listExpected).when(resourceTagDaoMock).listBy(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+
+ Arrays.asList(ResourceObjectType.values()).forEach(type -> {
+ Map result = presetVariableHelperSpy.getPresetVariableValueResourceTags(1l, type);
+
+ for (ResourceTag expected: listExpected) {
+ Assert.assertEquals(expected.getValue(), result.get(expected.getKey()));
+ }
+ });
+ }
+
+ @Test
+ public void loadPresetVariableValueForVolumeTestRecordIsNotAVolumeDoNothing() {
+ getQuotaTypesForTests(UsageTypes.VOLUME).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForVolume(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(volumeDaoMock);
+ }
+
+ @Test
+ public void loadPresetVariableValueForVolumeTestRecordIsVolumeAndHasStorageSetFields() {
+ Value expected = getValueForTests();
+
+ VolumeVO volumeVoMock = Mockito.mock(VolumeVO.class);
+ Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ Mockito.doReturn(1l).when(volumeVoMock).getPoolId();
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(volumeVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(volumeVoMock).getName();
+ Mockito.doReturn(expected.getDiskOffering()).when(presetVariableHelperSpy).getPresetVariableValueDiskOffering(Mockito.anyLong());
+ Mockito.doReturn(expected.getProvisioningType()).when(volumeVoMock).getProvisioningType();
+ Mockito.doReturn(expected.getStorage()).when(presetVariableHelperSpy).getPresetVariableValueStorage(Mockito.anyLong(), Mockito.anyInt());
+ Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+ Mockito.doReturn(expected.getSize()).when(volumeVoMock).getSize();
+
+ Mockito.doReturn(UsageTypes.VOLUME).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForVolume(usageVoMock, result);
+
+ Long expectedSize = ByteScaleUtils.bytesToMib(expected.getSize());
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getDiskOffering(), result.getDiskOffering());
+ Assert.assertEquals(expected.getProvisioningType(), result.getProvisioningType());
+ Assert.assertEquals(expected.getStorage(), result.getStorage());
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ Assert.assertEquals(expectedSize, result.getSize());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "diskOffering", "provisioningType", "storage", "tags", "size"), result);
+
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.eq(ResourceObjectType.Volume));
+ }
+
+ @Test
+ public void loadPresetVariableValueForVolumeTestRecordIsVolumeAndDoesNotHaveStorageSetFields() {
+ Value expected = getValueForTests();
+
+ VolumeVO volumeVoMock = Mockito.mock(VolumeVO.class);
+ Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ Mockito.doReturn(null).when(volumeVoMock).getPoolId();
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(volumeVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(volumeVoMock).getName();
+ Mockito.doReturn(expected.getDiskOffering()).when(presetVariableHelperSpy).getPresetVariableValueDiskOffering(Mockito.anyLong());
+ Mockito.doReturn(expected.getProvisioningType()).when(volumeVoMock).getProvisioningType();
+ Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+ Mockito.doReturn(expected.getSize()).when(volumeVoMock).getSize();
+
+ Mockito.doReturn(UsageTypes.VOLUME).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForVolume(usageVoMock, result);
+
+ Long expectedSize = ByteScaleUtils.bytesToMib(expected.getSize());
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getDiskOffering(), result.getDiskOffering());
+ Assert.assertEquals(expected.getProvisioningType(), result.getProvisioningType());
+ Assert.assertEquals(null, result.getStorage());
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ Assert.assertEquals(expectedSize, result.getSize());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "diskOffering", "provisioningType", "tags", "size"), result);
+
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.eq(ResourceObjectType.Volume));
+ }
+
+ @Test
+ public void getPresetVariableValueDiskOfferingTestSetValuesAndReturnObject() {
+ DiskOfferingVO diskOfferingVoMock = Mockito.mock(DiskOfferingVO.class);
+ Mockito.doReturn(diskOfferingVoMock).when(diskOfferingDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ GenericPresetVariable expected = getGenericPresetVariableForTests();
+ Mockito.doReturn(expected.getId()).when(diskOfferingVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(diskOfferingVoMock).getName();
+
+ GenericPresetVariable result = presetVariableHelperSpy.getPresetVariableValueDiskOffering(1l);
+
+ assertPresetVariableIdAndName(expected, result);
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name"), result);
+ }
+
+ @Test
+ public void getPresetVariableValueStorageTestGetSecondaryStorageForSnapshot() {
+ Storage expected = getStorageForTests();
+ Mockito.doReturn(expected).when(presetVariableHelperSpy).getSecondaryStorageForSnapshot(Mockito.anyLong(), Mockito.anyInt());
+
+ Storage result = presetVariableHelperSpy.getPresetVariableValueStorage(1l, 2);
+
+ Assert.assertEquals(expected, result);
+ Mockito.verify(primaryStorageDaoMock, Mockito.never()).findByIdIncludingRemoved(Mockito.anyLong());
+ }
+
+ @Test
+ public void getPresetVariableValueStorageTestGetSecondaryStorageForSnapshotReturnsNull() {
+ Storage expected = getStorageForTests();
+ Mockito.doReturn(null).when(presetVariableHelperSpy).getSecondaryStorageForSnapshot(Mockito.anyLong(), Mockito.anyInt());
+
+ StoragePoolVO storagePoolVoMock = Mockito.mock(StoragePoolVO.class);
+ Mockito.doReturn(storagePoolVoMock).when(primaryStorageDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+
+ Mockito.doReturn(expected.getId()).when(storagePoolVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(storagePoolVoMock).getName();
+ Mockito.doReturn(expected.getScope()).when(storagePoolVoMock).getScope();
+ Mockito.doReturn(expected.getTags()).when(storagePoolTagsDaoMock).getStoragePoolTags(Mockito.anyLong());
+
+ Storage result = presetVariableHelperSpy.getPresetVariableValueStorage(1l, 2);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getScope(), result.getScope());
+ Assert.assertEquals(expected.getTags(), result.getTags());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "scope", "tags"), result);
+ }
+
+ @Test
+ public void getSecondaryStorageForSnapshotTestAllTypesAndDoNotBackupSnapshotReturnNull() {
+ presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = false;
+ getQuotaTypesForTests().forEach(type -> {
+ Storage result = presetVariableHelperSpy.getSecondaryStorageForSnapshot(1l, type.getKey());
+ Assert.assertNull(result);
+ });
+ }
+
+ @Test
+ public void getSecondaryStorageForSnapshotTestAllTypesExceptSnapshotAndBackupSnapshotReturnNull() {
+ presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = true;
+ getQuotaTypesForTests(UsageTypes.SNAPSHOT).forEach(type -> {
+ Storage result = presetVariableHelperSpy.getSecondaryStorageForSnapshot(1l, type.getKey());
+ Assert.assertNull(result);
+ });
+ }
+
+ @Test
+ public void getSecondaryStorageForSnapshotTestRecordIsSnapshotAndBackupSnapshotSetFieldsAndReturnObject() {
+ Storage expected = getStorageForTests();
+
+ ImageStoreVO imageStoreVoMock = Mockito.mock(ImageStoreVO.class);
+ Mockito.doReturn(imageStoreVoMock).when(imageStoreDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(imageStoreVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(imageStoreVoMock).getName();
+ presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = true;
+
+ Storage result = presetVariableHelperSpy.getSecondaryStorageForSnapshot(1l, UsageTypes.SNAPSHOT);
+
+ assertPresetVariableIdAndName(expected, result);
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name"), result);
+ }
+
+ @Test
+ public void loadPresetVariableValueForTemplateAndIsoTestRecordIsNotAtemplateNorAnIsoDoNothing() {
+ getQuotaTypesForTests(templateAndIsoUsageTypes.toArray(new Integer[templateAndIsoUsageTypes.size()])).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForTemplateAndIso(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(vmTemplateDaoMock);
+ }
+
+
+ @Test
+ public void loadPresetVariableValueForTemplateAndIsoTestRecordIsVolumeSetFields() {
+ Value expected = getValueForTests();
+
+ VMTemplateVO vmTemplateVoMock = Mockito.mock(VMTemplateVO.class);
+ Mockito.doReturn(vmTemplateVoMock).when(vmTemplateDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(vmTemplateVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(vmTemplateVoMock).getName();
+ Mockito.doReturn(expected.getOsName()).when(presetVariableHelperSpy).getPresetVariableValueOsName(Mockito.anyLong());
+ Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+ Mockito.doReturn(expected.getSize()).when(vmTemplateVoMock).getSize();
+
+ templateAndIsoUsageTypes.forEach(type -> {
+ Mockito.doReturn(type).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForTemplateAndIso(usageVoMock, result);
+
+ Long expectedSize = ByteScaleUtils.bytesToMib(expected.getSize());
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getOsName(), result.getOsName());
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ Assert.assertEquals(expectedSize, result.getSize());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "osName", "tags", "size"), result);
+ });
+
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.eq(ResourceObjectType.Template));
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.eq(ResourceObjectType.ISO));
+ }
+
+
+ @Test
+ public void loadPresetVariableValueForSnapshotTestRecordIsNotASnapshotDoNothing() {
+ getQuotaTypesForTests(UsageTypes.SNAPSHOT).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForSnapshot(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(snapshotDaoMock);
+ }
+
+
+ @Test
+ public void loadPresetVariableValueForSnapshotTestRecordIsSnapshotSetFields() {
+ Value expected = getValueForTests();
+
+ SnapshotVO snapshotVoMock = Mockito.mock(SnapshotVO.class);
+ Mockito.doReturn(snapshotVoMock).when(snapshotDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(snapshotVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(snapshotVoMock).getName();
+ Mockito.doReturn(expected.getSize()).when(snapshotVoMock).getSize();
+ Mockito.doReturn((short) 3).when(snapshotVoMock).getSnapshotType();
+ Mockito.doReturn(1l).when(presetVariableHelperSpy).getSnapshotDataStoreId(Mockito.anyLong());
+ Mockito.doReturn(expected.getStorage()).when(presetVariableHelperSpy).getPresetVariableValueStorage(Mockito.anyLong(), Mockito.anyInt());
+ Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+
+ Mockito.doReturn(UsageTypes.SNAPSHOT).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForSnapshot(usageVoMock, result);
+
+ Long expectedSize = ByteScaleUtils.bytesToMib(expected.getSize());
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getSnapshotType(), result.getSnapshotType());
+ Assert.assertEquals(expected.getStorage(), result.getStorage());
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ Assert.assertEquals(expectedSize, result.getSize());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "snapshotType", "storage", "tags", "size"), result);
+
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.eq(ResourceObjectType.Snapshot));
+ }
+
+
+ @Test
+ public void getSnapshotDataStoreIdTestDoNotBackupSnapshotToSecondaryRetrievePrimaryStorage() {
+ SnapshotDataStoreVO snapshotDataStoreVoMock = Mockito.mock(SnapshotDataStoreVO.class);
+
+ Long expected = 1l;
+ Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class));
+ Mockito.doReturn(expected).when(snapshotDataStoreVoMock).getDataStoreId();
+ presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = false;
+
+ Long result = presetVariableHelperSpy.getSnapshotDataStoreId(1l);
+
+ Assert.assertEquals(expected, result);
+
+ Arrays.asList(DataStoreRole.values()).forEach(role -> {
+ if (role == DataStoreRole.Primary) {
+ Mockito.verify(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
+ } else {
+ Mockito.verify(snapshotDataStoreDaoMock, Mockito.never()).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
+ }
+ });
+ }
+
+ @Test
+ public void getSnapshotDataStoreIdTestBackupSnapshotToSecondaryRetrieveSecondaryStorage() {
+ SnapshotDataStoreVO snapshotDataStoreVoMock = Mockito.mock(SnapshotDataStoreVO.class);
+
+ Long expected = 2l;
+ Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class));
+ Mockito.doReturn(expected).when(snapshotDataStoreVoMock).getDataStoreId();
+ presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = true;
+
+ Long result = presetVariableHelperSpy.getSnapshotDataStoreId(2l);
+
+ Assert.assertEquals(expected, result);
+
+ Arrays.asList(DataStoreRole.values()).forEach(role -> {
+ if (role == DataStoreRole.Image) {
+ Mockito.verify(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
+ } else {
+ Mockito.verify(snapshotDataStoreDaoMock, Mockito.never()).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
+ }
+ });
+ }
+
+ @Test
+ public void loadPresetVariableValueForNetworkOfferingTestRecordIsNotASnapshotDoNothing() {
+ getQuotaTypesForTests(UsageTypes.NETWORK_OFFERING).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForNetworkOffering(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(networkOfferingDaoMock);
+ }
+
+ @Test
+ public void loadPresetVariableValueForNetworkOfferingTestRecordIsSnapshotSetFields() {
+ Value expected = getValueForTests();
+
+ NetworkOfferingVO networkOfferingVoMock = Mockito.mock(NetworkOfferingVO.class);
+ Mockito.doReturn(networkOfferingVoMock).when(networkOfferingDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(networkOfferingVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(networkOfferingVoMock).getName();
+ Mockito.doReturn(expected.getTag()).when(networkOfferingVoMock).getTags();
+
+ Mockito.doReturn(UsageTypes.NETWORK_OFFERING).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForNetworkOffering(usageVoMock, result);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getTag(), result.getTag());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "tag"), result);
+ }
+
+ @Test
+ public void loadPresetVariableValueForVmSnapshotTestRecordIsNotAVmSnapshotDoNothing() {
+ getQuotaTypesForTests(UsageTypes.VM_SNAPSHOT).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForVmSnapshot(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(vmSnapshotDaoMock);
+ }
+
+ @Test
+ public void loadPresetVariableValueForVmSnapshotTestRecordIsVmSnapshotSetFields() {
+ Value expected = getValueForTests();
+
+ VMSnapshotVO vmSnapshotVoMock = Mockito.mock(VMSnapshotVO.class);
+ Mockito.doReturn(vmSnapshotVoMock).when(vmSnapshotDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getId()).when(vmSnapshotVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(vmSnapshotVoMock).getName();
+ Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
+ Mockito.doReturn(expected.getVmSnapshotType()).when(vmSnapshotVoMock).getType();
+
+ Mockito.doReturn(UsageTypes.VM_SNAPSHOT).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForVmSnapshot(usageVoMock, result);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getTags(), result.getTags());
+ Assert.assertEquals(expected.getVmSnapshotType(), result.getVmSnapshotType());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "tags", "vmSnapshotType"), result);
+
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.eq(ResourceObjectType.VMSnapshot));
+ }
+
+ @Test (expected = CloudRuntimeException.class)
+ public void validateIfObjectIsNullTestObjectIsNullThrowException() {
+ presetVariableHelperSpy.validateIfObjectIsNull(null, null, null);
+ }
+
+ @Test
+ public void validateIfObjectIsNullTestObjectIsNotNullDoNothing() {
+ presetVariableHelperSpy.validateIfObjectIsNull(new Object(), null, null);
+ }
+
+ @Test
+ public void setPresetVariableValueServiceOfferingAndComputingResourcesTestSetComputingResourcesOnlyToRunningVm() {
+ Value expected = getValueForTests();
+
+ Mockito.doReturn(serviceOfferingVoMock).when(serviceOfferingDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getComputeOffering()).when(presetVariableHelperSpy).getPresetVariableValueComputeOffering(Mockito.any());
+ Mockito.doReturn(expected.getComputingResources()).when(presetVariableHelperSpy).getPresetVariableValueComputingResource(Mockito.any(), Mockito.any());
+
+ QuotaTypes.listQuotaTypes().forEach((typeInt, value) -> {
+
+ Value result = new Value();
+ presetVariableHelperSpy.setPresetVariableValueServiceOfferingAndComputingResources(result, typeInt, vmInstanceVoMock);
+
+ Assert.assertEquals(expected.getComputeOffering(), result.getComputeOffering());
+
+ if (typeInt == UsageTypes.RUNNING_VM) {
+ Assert.assertEquals(expected.getComputingResources(), result.getComputingResources());
+ validateFieldNamesToIncludeInToString(Arrays.asList("computeOffering", "computingResources"), result);
+ } else {
+ validateFieldNamesToIncludeInToString(Arrays.asList("computeOffering"), result);
+ }
+ });
+ }
+
+ @Test
+ public void getDetailByNameTestKeyDoesNotExistsReturnsDefaultValue() {
+ int expected = 874;
+ int result = presetVariableHelperSpy.getDetailByName(getVmDetailsForTests(), "detail_that_does_not_exist", expected);
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getDetailByNameTestValueIsNullReturnsDefaultValue() {
+ int expected = 749;
+ int result = presetVariableHelperSpy.getDetailByName(getVmDetailsForTests(), "test_with_null", expected);
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test(expected = NumberFormatException.class)
+ public void getDetailByNameTestValueIsInvalidThrowsNumberFormatException() {
+ presetVariableHelperSpy.getDetailByName(getVmDetailsForTests(), "test_with_invalid_value", 0);
+ }
+
+ @Test
+ public void getDetailByNameTestReturnsValue() {
+ int expected = Integer.valueOf(getVmDetailsForTests().get(0).getValue());
+ int result = presetVariableHelperSpy.getDetailByName(getVmDetailsForTests(), "test_with_value", expected);
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getPresetVariableValueComputingResourceTestServiceOfferingIsNotDynamicReturnsServiceOfferingValues() {
+ ComputingResources expected = getComputingResourcesForTests();
+
+ Mockito.doReturn(expected.getCpuNumber()).when(serviceOfferingVoMock).getCpu();
+ Mockito.doReturn(expected.getCpuSpeed()).when(serviceOfferingVoMock).getSpeed();
+ Mockito.doReturn(expected.getMemory()).when(serviceOfferingVoMock).getRamSize();
+ Mockito.doReturn(false).when(serviceOfferingVoMock).isDynamic();
+
+ ComputingResources result = presetVariableHelperSpy.getPresetVariableValueComputingResource(vmInstanceVoMock, serviceOfferingVoMock);
+
+ Assert.assertEquals(expected.toString(), result.toString());
+ Mockito.verify(userVmDetailsDaoMock, Mockito.never()).listDetails(Mockito.anyLong());
+ }
+
+ @Test
+ public void getPresetVariableValueComputingResourceTestServiceOfferingIsDynamicReturnsVmDetails() {
+ ComputingResources expected = getComputingResourcesForTests();
+
+ Mockito.doReturn(1).when(serviceOfferingVoMock).getCpu();
+ Mockito.doReturn(2).when(serviceOfferingVoMock).getSpeed();
+ Mockito.doReturn(3).when(serviceOfferingVoMock).getRamSize();
+ Mockito.doReturn(true).when(serviceOfferingVoMock).isDynamic();
+
+ Mockito.doReturn(expected.getMemory(), expected.getCpuNumber(), expected.getCpuSpeed()).when(presetVariableHelperSpy).getDetailByName(Mockito.anyList(),
+ Mockito.anyString(), Mockito.anyInt());
+
+ ComputingResources result = presetVariableHelperSpy.getPresetVariableValueComputingResource(vmInstanceVoMock, serviceOfferingVoMock);
+
+ Assert.assertEquals(expected.toString(), result.toString());
+ Mockito.verify(userVmDetailsDaoMock).listDetails(Mockito.anyLong());
+ }
+
+ @Test
+ public void loadPresetVariableValueForBackupTestRecordIsNotABackupDoNothing() {
+ getQuotaTypesForTests(QuotaTypes.BACKUP).forEach(type -> {
+ Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
+ presetVariableHelperSpy.loadPresetVariableValueForBackup(usageVoMock, null);
+ });
+
+ Mockito.verifyNoInteractions(backupOfferingDaoMock);
+ }
+
+ @Test
+ public void loadPresetVariableValueForBackupTestRecordIsBackupSetAllFields() {
+ Value expected = getValueForTests();
+
+ mockMethodValidateIfObjectIsNull();
+
+ Mockito.doReturn(expected.getSize()).when(usageVoMock).getSize();
+ Mockito.doReturn(expected.getVirtualSize()).when(usageVoMock).getVirtualSize();
+ Mockito.doReturn(expected.getBackupOffering()).when(presetVariableHelperSpy).getPresetVariableValueBackupOffering(Mockito.anyLong());
+
+ Mockito.doReturn(QuotaTypes.BACKUP).when(usageVoMock).getUsageType();
+
+ Value result = new Value();
+ presetVariableHelperSpy.loadPresetVariableValueForBackup(usageVoMock, result);
+
+ Assert.assertEquals(expected.getSize(), result.getSize());
+ Assert.assertEquals(expected.getVirtualSize(), result.getVirtualSize());
+ Assert.assertEquals(expected.getBackupOffering(), result.getBackupOffering());
+
+ validateFieldNamesToIncludeInToString(Arrays.asList("size", "virtualSize", "backupOffering"), result);
+
+ Mockito.verify(presetVariableHelperSpy).getPresetVariableValueBackupOffering(Mockito.anyLong());
+ }
+
+ @Test
+ public void getPresetVariableValueBackupOfferingTestSetValuesAndReturnObject() {
+ BackupOfferingVO backupOfferingVoMock = Mockito.mock(BackupOfferingVO.class);
+ Mockito.doReturn(backupOfferingVoMock).when(backupOfferingDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
+ mockMethodValidateIfObjectIsNull();
+
+ BackupOffering expected = getBackupOfferingForTests();
+ Mockito.doReturn(expected.getId()).when(backupOfferingVoMock).getUuid();
+ Mockito.doReturn(expected.getName()).when(backupOfferingVoMock).getName();
+ Mockito.doReturn(expected.getExternalId()).when(backupOfferingVoMock).getExternalId();
+
+ BackupOffering result = presetVariableHelperSpy.getPresetVariableValueBackupOffering(1l);
+
+ assertPresetVariableIdAndName(expected, result);
+ Assert.assertEquals(expected.getExternalId(), result.getExternalId());
+ validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "externalId"), result);
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java
new file mode 100644
index 00000000000..cdcfc87cd4e
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java
@@ -0,0 +1,40 @@
+// 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.activationrule.presetvariables;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ResourceTest {
+
+ @Test
+ public void toStringTestReturnAJson() {
+ Resource variable = new Resource();
+
+ String expected = ToStringBuilder.reflectionToString(variable, ToStringStyle.JSON_STYLE);
+ String result = variable.toString();
+
+ Assert.assertEquals(expected, result);
+ }
+
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java
new file mode 100644
index 00000000000..88265ee4e55
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java
@@ -0,0 +1,34 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RoleTest {
+
+ @Test
+ public void setTagsTestAddFieldTagsToCollection() {
+ Role variable = new Role();
+ variable.setType(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("type"));
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/StorageTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/StorageTest.java
new file mode 100644
index 00000000000..f36d5c49581
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/StorageTest.java
@@ -0,0 +1,41 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class StorageTest {
+
+ @Test
+ public void setTagsTestAddFieldTagsToCollection() {
+ Storage variable = new Storage();
+ variable.setTags(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("tags"));
+ }
+
+ @Test
+ public void setScopeTestAddFieldScopeToCollection() {
+ Storage variable = new Storage();
+ variable.setScope(null);;
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("scope"));
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java
new file mode 100644
index 00000000000..c9d14401b3f
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java
@@ -0,0 +1,139 @@
+// 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.activationrule.presetvariables;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ValueTest {
+
+ @Test
+ public void setHostTestAddFieldHostToCollection() {
+ Value variable = new Value();
+ variable.setHost(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("host"));
+ }
+
+ @Test
+ public void setOsNameTestAddFieldOsNameToCollection() {
+ Value variable = new Value();
+ variable.setOsName(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("osName"));
+ }
+
+ @Test
+ public void setAccountResourcesTestAddFieldAccountResourcesToCollection() {
+ Value variable = new Value();
+ variable.setAccountResources(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("accountResources"));
+ }
+
+ @Test
+ public void setTagsTestAddFieldTagsToCollection() {
+ Value variable = new Value();
+ variable.setTags(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("tags"));
+ }
+
+ @Test
+ public void setTagTestAddFieldTagToCollection() {
+ Value variable = new Value();
+ variable.setTag(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("tag"));
+ }
+
+ @Test
+ public void setSizeTestAddFieldSizeToCollection() {
+ Value variable = new Value();
+ variable.setSize(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("size"));
+ }
+
+ @Test
+ public void setProvisioningTypeTestAddFieldProvisioningTypeToCollection() {
+ Value variable = new Value();
+ variable.setProvisioningType(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("provisioningType"));
+ }
+
+ @Test
+ public void setSnapshotTypeTestAddFieldSnapshotTypeToCollection() {
+ Value variable = new Value();
+ variable.setSnapshotType(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("snapshotType"));
+ }
+
+ @Test
+ public void setVmSnapshotTypeTestAddFieldVmSnapshotTypeToCollection() {
+ Value variable = new Value();
+ variable.setVmSnapshotType(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("vmSnapshotType"));
+ }
+
+ @Test
+ public void setComputeOfferingTestAddFieldComputeOfferingToCollection() {
+ Value variable = new Value();
+ variable.setComputeOffering(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("computeOffering"));
+ }
+
+ @Test
+ public void setTemplateTestAddFieldTemplateToCollection() {
+ Value variable = new Value();
+ variable.setTemplate(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("template"));
+ }
+
+ @Test
+ public void setDiskOfferingTestAddFieldDiskOfferingToCollection() {
+ Value variable = new Value();
+ variable.setDiskOffering(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("diskOffering"));
+ }
+
+ @Test
+ public void setStorageTestAddFieldStorageToCollection() {
+ Value variable = new Value();
+ variable.setStorage(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("storage"));
+ }
+
+ @Test
+ public void setComputingResourcesTestAddFieldComputingResourcesToCollection() {
+ Value variable = new Value();
+ variable.setComputingResources(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("computingResources"));
+ }
+
+ @Test
+ public void setVirtualSizeTestAddFieldVirtualSizeToCollection() {
+ Value variable = new Value();
+ variable.setVirtualSize(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("virtualSize"));
+ }
+
+ @Test
+ public void setBackupOfferingTestAddFieldBackupOfferingToCollection() {
+ Value variable = new Value();
+ variable.setBackupOffering(null);
+ Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("backupOffering"));
+ }
+}
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/constant/QuotaTypesTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/constant/QuotaTypesTest.java
index 427043951a1..65f95faa706 100644
--- a/framework/quota/src/test/java/org/apache/cloudstack/quota/constant/QuotaTypesTest.java
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/constant/QuotaTypesTest.java
@@ -43,6 +43,6 @@ public class QuotaTypesTest extends TestCase {
@Test
public void testQuotaTypeDescription() {
assertNull(QuotaTypes.getDescription(-1));
- assertNotNull(QuotaTypes.getDescription(QuotaTypes.MEMORY));
+ assertNotNull(QuotaTypes.getDescription(QuotaTypes.VOLUME));
}
}
\ No newline at end of file
diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/vo/QuotaTariffVOTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/vo/QuotaTariffVOTest.java
new file mode 100644
index 00000000000..2441df921b2
--- /dev/null
+++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/vo/QuotaTariffVOTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed 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.vo;
+
+import java.util.Map;
+
+import org.apache.cloudstack.quota.constant.QuotaTypes;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class QuotaTariffVOTest {
+
+ @Test
+ public void setUsageTypeDataTestSetAllDataAndReturnTrueToAllexistingQuotaType() {
+ QuotaTariffVO quotaTariffVoTest = new QuotaTariffVO();
+
+ for (Map.Entry quotaType: QuotaTypes.listQuotaTypes().entrySet()) {
+ boolean result = quotaTariffVoTest.setUsageTypeData(quotaType.getKey());
+
+ Assert.assertTrue(result);
+ Assert.assertEquals((int) quotaType.getKey(), quotaTariffVoTest.getUsageType());
+ Assert.assertEquals(quotaType.getValue().getQuotaName(), quotaTariffVoTest.getUsageName());
+ Assert.assertEquals(quotaType.getValue().getQuotaUnit(), quotaTariffVoTest.getUsageUnit());
+ Assert.assertEquals(quotaType.getValue().getDiscriminator(), quotaTariffVoTest.getUsageDiscriminator());
+ }
+ }
+
+ @Test
+ public void setUsageTypeDataTestReturnFalseToInvalidUsageType() {
+ QuotaTariffVO quotaTariffVoTest = new QuotaTariffVO();
+
+ boolean result = quotaTariffVoTest.setUsageTypeData(0);
+
+ Assert.assertFalse(result);
+ }
+}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java
new file mode 100644
index 00000000000..e7dc8654564
--- /dev/null
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java
@@ -0,0 +1,153 @@
+//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.Account;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+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.vo.QuotaTariffVO;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+
+import java.util.Date;
+
+@APICommand(name = QuotaTariffCreateCmd.API_NAME, responseObject = QuotaTariffResponse.class, description = "Creates a quota tariff for a resource.", since = "4.17.0.0",
+requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin})
+public class QuotaTariffCreateCmd extends BaseCmd {
+ protected Logger logger = Logger.getLogger(getClass());
+ public static final String API_NAME = "quotaTariffCreate";
+
+ @Inject
+ QuotaResponseBuilder responseBuilder;
+
+ @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Quota tariff's name", length = 32)
+ private String name;
+
+ @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "Quota tariff's description.", length = 256)
+ private String description;
+
+ @Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = true, description = "Integer value for the usage type of the resource.")
+ private Integer usageType;
+
+ @Parameter(name = "value", type = CommandType.DOUBLE, required = true, description = "The quota tariff value of the resource as per the default unit.")
+ private Double value;
+
+ @Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, description = "Quota tariff's activation rule.",
+ length = 65535)
+ private String activationRule;
+
+ @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "The effective start date on/after which the quota tariff is effective. Use yyyy-MM-dd as"
+ + " the date format, e.g. startDate=2009-06-03. Inform null to use the current date.")
+ private Date startDate;
+
+ @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "The end date of the quota tariff. Use yyyy-MM-dd as the date format, e.g."
+ + " endDate=2009-06-03.")
+ private Date endDate;
+
+ public QuotaTariffCreateCmd() {
+ super();
+ }
+
+ @Override
+ public String getCommandName() {
+ return QuotaTariffCreateCmd.API_NAME.toLowerCase() + RESPONSE_SUFFIX;
+ }
+
+ @Override
+ public void execute() {
+ QuotaTariffVO result = responseBuilder.createQuotaTariff(this);
+
+ if (result == null) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create new quota tariff.");
+ }
+
+ QuotaTariffResponse response = responseBuilder.createQuotaTariffResponse(result);
+ response.setResponseName(getCommandName());
+ setResponseObject(response);
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return Account.ACCOUNT_ID_SYSTEM;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getUsageType() {
+ return usageType;
+ }
+
+ public void setUsageType(Integer usageType) {
+ this.usageType = usageType;
+ }
+
+ public Double getValue() {
+ return value;
+ }
+
+ public void setValue(Double value) {
+ this.value = value;
+ }
+
+ public String getActivationRule() {
+ return activationRule;
+ }
+
+ public void setActivationRule(String activationRule) {
+ this.activationRule = activationRule;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffDeleteCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffDeleteCmd.java
new file mode 100644
index 00000000000..0ccbd0a9647
--- /dev/null
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffDeleteCmd.java
@@ -0,0 +1,77 @@
+//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.Account;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+import org.apache.cloudstack.api.response.QuotaTariffResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaTariffDeleteCmd.API_NAME, description = "Marks a quota tariff as removed.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false,
+responseHasSensitiveInfo = false, since = "4.17.0.0", authorized = {RoleType.Admin})
+public class QuotaTariffDeleteCmd extends BaseCmd {
+ public static final String API_NAME = "quotaTariffDelete";
+ protected Logger logger = Logger.getLogger(getClass());
+
+ @Inject
+ QuotaResponseBuilder responseBuilder;
+
+ @Parameter(name = ApiConstants.UUID, type = BaseCmd.CommandType.STRING, required = true, entityType = QuotaTariffResponse.class,
+ description = "UUID of the quota tariff", validations = {ApiArgValidator.UuidString})
+ private String quotaTariffUuid;
+
+ public String getQuotaTariffUuid() {
+ return quotaTariffUuid;
+ }
+
+ public void setQuotaTariffId(String quotaTariffUuid) {
+ this.quotaTariffUuid = quotaTariffUuid;
+ }
+
+ public QuotaTariffDeleteCmd() {
+ super();
+ }
+
+ @Override
+ public String getCommandName() {
+ return QuotaTariffDeleteCmd.API_NAME.toLowerCase() + RESPONSE_SUFFIX;
+ }
+
+ @Override
+ public void execute() {
+ boolean result = responseBuilder.deleteQuotaTariff(getQuotaTariffUuid());
+ SuccessResponse response = new SuccessResponse(getCommandName());
+ response.setSuccess(result);
+ setResponseObject(response);
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return Account.ACCOUNT_ID_SYSTEM;
+ }
+
+}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java
index c1368104ff3..b3d1134daa6 100644
--- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffListCmd.java
@@ -27,6 +27,7 @@ import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.api.response.QuotaTariffResponse;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.log4j.Logger;
import javax.inject.Inject;
@@ -46,9 +47,21 @@ public class QuotaTariffListCmd extends BaseListCmd {
@Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = false, description = "Usage type of the resource")
private Integer usageType;
- @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "The effective start date on/after which the quota tariff is effective and older tariffs are no longer used for the usage type. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.")
+ @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "The start date of the quota tariff. Use yyyy-MM-dd as the date format, "
+ + "e.g. startDate=2009-06-03.")
private Date effectiveDate;
+ @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, description = "The end date of the quota tariff. Use yyyy-MM-dd as the date format, e.g. "
+ + "endDate=2021-11-03.")
+ private Date endDate;
+
+ @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "The name of the quota tariff.")
+ private String name;
+
+ @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, required = false, description = "False will list only not removed quota tariffs. If set to True, we will "
+ + "list all, including the removed ones. The default is false.")
+ private boolean listAll = false;
+
public QuotaTariffListCmd() {
super();
}
@@ -58,10 +71,10 @@ public class QuotaTariffListCmd extends BaseListCmd {
final Pair, Integer> result = _responseBuilder.listQuotaTariffPlans(this);
final List responses = new ArrayList();
+
+ s_logger.trace(String.format("Adding quota tariffs [%s] to response of API quotaTariffList.", ReflectionToStringBuilderUtils.reflectCollection(responses)));
+
for (final QuotaTariffVO resource : result.first()) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Result desc=" + resource.getDescription() + " date=" + resource.getEffectiveOn() + " val=" + resource.getCurrencyValue());
- }
responses.add(_responseBuilder.createQuotaTariffResponse(resource));
}
@@ -93,4 +106,16 @@ public class QuotaTariffListCmd extends BaseListCmd {
this.usageType = usageType;
}
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isListAll() {
+ return listAll;
+ }
+
}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java
index 42aa825b094..5d6b11c40a3 100644
--- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java
@@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command;
import com.cloud.user.Account;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
@@ -33,7 +34,8 @@ import javax.inject.Inject;
import java.util.Date;
-@APICommand(name = "quotaTariffUpdate", responseObject = QuotaTariffResponse.class, description = "Update the tariff plan for a resource", since = "4.7.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+@APICommand(name = "quotaTariffUpdate", responseObject = QuotaTariffResponse.class, description = "Update the tariff plan for a resource", since = "4.7.0",
+requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin})
public class QuotaTariffUpdateCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(QuotaTariffUpdateCmd.class);
private static final String s_name = "quotatariffupdateresponse";
@@ -41,20 +43,34 @@ public class QuotaTariffUpdateCmd extends BaseCmd {
@Inject
QuotaResponseBuilder _responseBuilder;
- @Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = true, description = "Integer value for the usage type of the resource")
+ @Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, description = "Integer value for the usage type of the resource")
private Integer usageType;
- @Parameter(name = "value", type = CommandType.DOUBLE, required = true, description = "The quota tariff value of the resource as per the default unit")
+ @Parameter(name = ApiConstants.VALUE, type = CommandType.DOUBLE, description = "The quota tariff value of the resource as per the default unit.")
private Double value;
- @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = true, description = "The effective start date on/after which the quota tariff is effective and older tariffs are no longer used for the usage type. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.")
+ @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "The effective start date on/after which the quota tariff is effective. Use yyyy-MM-dd as"
+ + " the date format, e.g. startDate=2009-06-03.")
private Date startDate;
- public int getUsageType() {
+ @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "The end date of the quota tariff. Use yyyy-MM-dd as the date format, e.g."
+ + " endDate=2009-06-03.")
+ private Date endDate;
+
+ @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Quota tariff's name")
+ private String name;
+
+ @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "Quota tariff's description. Inform empty to remove the description.")
+ private String description;
+
+ @Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, description = "Quota tariff's activation rule. Inform empty to remove the activation rule.")
+ private String activationRule;
+
+ public Integer getUsageType() {
return usageType;
}
- public void setUsageType(int usageType) {
+ public void setUsageType(Integer usageType) {
this.usageType = usageType;
}
@@ -74,6 +90,22 @@ public class QuotaTariffUpdateCmd extends BaseCmd {
this.startDate = startDate == null ? null : new Date(startDate.getTime());
}
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getActivationRule() {
+ return activationRule;
+ }
+
public QuotaTariffUpdateCmd() {
super();
}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
index bde432c1f69..36033043bcf 100644
--- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
@@ -20,6 +20,7 @@ import org.apache.cloudstack.api.command.QuotaBalanceCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
+import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
@@ -64,4 +65,8 @@ public interface QuotaResponseBuilder {
Date startOfNextDay(Date dt);
Date startOfNextDay();
+
+ QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd);
+
+ boolean deleteQuotaTariff(String quotaTariffUuid);
}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
index 224a37c660f..0227b37eee8 100644
--- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
@@ -30,13 +30,17 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import java.util.function.Consumer;
import javax.inject.Inject;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
+import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
import org.apache.cloudstack.quota.QuotaManager;
@@ -56,6 +60,7 @@ 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.quota.vo.QuotaUsageVO;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -113,8 +118,14 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
response.setUsageDiscriminator(tariff.getUsageDiscriminator());
response.setTariffValue(tariff.getCurrencyValue());
response.setEffectiveOn(tariff.getEffectiveOn());
- response.setDescription(tariff.getDescription());
+ response.setUsageTypeDescription(tariff.getUsageTypeDescription());
response.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
+ response.setActivationRule(tariff.getActivationRule());
+ response.setName(tariff.getName());
+ response.setEndDate(tariff.getEndDate());
+ response.setDescription(tariff.getDescription());
+ response.setUuid(tariff.getUuid());
+ response.setRemoved(tariff.getRemoved());
return response;
}
@@ -354,58 +365,119 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@Override
public Pair, Integer> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
- Pair, Integer> result;
- Date effectiveDate = cmd.getEffectiveDate() == null ? new Date() : cmd.getEffectiveDate();
- Date adjustedEffectiveDate = _quotaService.computeAdjustedTime(effectiveDate);
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Effective datec=" + effectiveDate + " quotatype=" + cmd.getUsageType() + " Adjusted date=" + adjustedEffectiveDate);
- }
- if (cmd.getUsageType() != null) {
- QuotaTariffVO tariffPlan = _quotaTariffDao.findTariffPlanByUsageType(cmd.getUsageType(), adjustedEffectiveDate);
- if (tariffPlan != null) {
- List list = new ArrayList<>();
- list.add(tariffPlan);
- result = new Pair<>(list, list.size());
- } else {
- result = new Pair<>(new ArrayList<>(), 0);
- }
- } else {
- result = _quotaTariffDao.listAllTariffPlans(adjustedEffectiveDate, cmd.getStartIndex(), cmd.getPageSizeVal());
- }
- return result;
+ Date startDate = _quotaService.computeAdjustedTime(cmd.getEffectiveDate());
+ Date endDate = _quotaService.computeAdjustedTime(cmd.getEndDate());
+ Integer usageType = cmd.getUsageType();
+ String name = cmd.getName();
+ boolean listAll = cmd.isListAll();
+ Long startIndex = cmd.getStartIndex();
+ Long pageSize = cmd.getPageSizeVal();
+
+ s_logger.debug(String.format("Listing quota tariffs for parameters [%s].", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "effectiveDate",
+ "endDate", "listAll", "name", "page", "pageSize", "usageType")));
+
+ return _quotaTariffDao.listQuotaTariffs(startDate, endDate, usageType, name, null, listAll, startIndex, pageSize);
}
@Override
public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) {
- final int quotaType = cmd.getUsageType();
- final BigDecimal quotaCost = new BigDecimal(cmd.getValue());
- final Date effectiveDate = _quotaService.computeAdjustedTime(cmd.getStartDate());
- final Date now = _quotaService.computeAdjustedTime(new Date());
- // if effective date is in the past return error
- if (effectiveDate.compareTo(now) < 0) {
- throw new InvalidParameterValueException("Incorrect effective date for tariff " + effectiveDate + " is less than now " + now);
- }
- QuotaTypes quotaConstant = QuotaTypes.listQuotaTypes().get(quotaType);
- if (quotaConstant == null) {
- throw new InvalidParameterValueException("Quota type does not exists " + quotaType);
+ String name = cmd.getName();
+ Double value = cmd.getValue();
+ Date endDate = _quotaService.computeAdjustedTime(cmd.getEndDate());
+ String description = cmd.getDescription();
+ String activationRule = cmd.getActivationRule();
+ Date now = _quotaService.computeAdjustedTime(new Date());
+
+ warnQuotaTariffUpdateDeprecatedFields(cmd);
+
+ QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name);
+
+ if (currentQuotaTariff == null) {
+ throw new InvalidParameterValueException(String.format("There is no quota tariffs with name [%s].", name));
}
- QuotaTariffVO result = null;
- result = new QuotaTariffVO(quotaType);
- result.setUsageName(quotaConstant.getQuotaName());
- result.setUsageUnit(quotaConstant.getQuotaUnit());
- result.setUsageDiscriminator(quotaConstant.getDiscriminator());
- result.setCurrencyValue(quotaCost);
- result.setEffectiveOn(effectiveDate);
- result.setUpdatedOn(now);
- result.setUpdatedBy(cmd.getEntityOwnerId());
+ Date currentQuotaTariffStartDate = currentQuotaTariff.getEffectiveOn();
- if (s_logger.isDebugEnabled()) {
- s_logger.debug(String.format("Updating Quota Tariff Plan: New value=%s for resource type=%d effective on date=%s", quotaCost, quotaType, effectiveDate));
+ currentQuotaTariff.setRemoved(now);
+
+ QuotaTariffVO newQuotaTariff = persistNewQuotaTariff(currentQuotaTariff, name, 0, currentQuotaTariffStartDate, cmd.getEntityOwnerId(), endDate, value, description,
+ activationRule);
+ _quotaTariffDao.updateQuotaTariff(currentQuotaTariff);
+ return newQuotaTariff;
+ }
+
+ protected void warnQuotaTariffUpdateDeprecatedFields(QuotaTariffUpdateCmd cmd) {
+ String warnMessage = "The parameter 's%s' for API 'quotaTariffUpdate' is no longer needed and it will be removed in future releases.";
+
+ if (cmd.getStartDate() != null) {
+ s_logger.warn(String.format(warnMessage,"startdate"));
}
- _quotaTariffDao.addQuotaTariff(result);
- return result;
+ if (cmd.getUsageType() != null) {
+ s_logger.warn(String.format(warnMessage,"usagetype"));
+ }
+ }
+
+ protected QuotaTariffVO persistNewQuotaTariff(QuotaTariffVO currentQuotaTariff, String name, int usageType, Date startDate, Long entityOwnerId, Date endDate, Double value,
+ String description, String activationRule) {
+
+ QuotaTariffVO newQuotaTariff = getNewQuotaTariffObject(currentQuotaTariff, name, usageType);
+
+ newQuotaTariff.setEffectiveOn(startDate);
+ newQuotaTariff.setUpdatedOn(startDate);
+ newQuotaTariff.setUpdatedBy(entityOwnerId);
+
+ validateEndDateOnCreatingNewQuotaTariff(newQuotaTariff, startDate, endDate);
+ validateValueOnCreatingNewQuotaTariff(newQuotaTariff, value);
+ validateStringsOnCreatingNewQuotaTariff(newQuotaTariff::setDescription, description);
+ validateStringsOnCreatingNewQuotaTariff(newQuotaTariff::setActivationRule, activationRule);
+
+ _quotaTariffDao.addQuotaTariff(newQuotaTariff);
+ return newQuotaTariff;
+ }
+
+ protected QuotaTariffVO getNewQuotaTariffObject(QuotaTariffVO currentQuotaTariff, String name, int usageType) {
+ if (currentQuotaTariff != null) {
+ return new QuotaTariffVO(currentQuotaTariff);
+ }
+
+ QuotaTariffVO newQuotaTariff = new QuotaTariffVO();
+
+ if (!newQuotaTariff.setUsageTypeData(usageType)) {
+ throw new InvalidParameterValueException(String.format("There is no usage type with value [%s].", usageType));
+ }
+
+ newQuotaTariff.setName(name);
+ return newQuotaTariff;
+ }
+
+ protected void validateStringsOnCreatingNewQuotaTariff(Consumer method, String value){
+ if (value != null) {
+ method.accept(value.isBlank() ? null : value);
+ }
+ }
+
+ protected void validateValueOnCreatingNewQuotaTariff(QuotaTariffVO newQuotaTariff, Double value) {
+ if (value != null) {
+ newQuotaTariff.setCurrencyValue(BigDecimal.valueOf(value));
+ }
+ }
+
+ protected void validateEndDateOnCreatingNewQuotaTariff(QuotaTariffVO newQuotaTariff, Date startDate, Date endDate) {
+ if (endDate == null) {
+ return;
+ }
+
+ if (endDate.compareTo(startDate) < 0) {
+ throw new InvalidParameterValueException(String.format("The quota tariff's end date [%s] cannot be less than the start date [%s]", endDate, startDate));
+ }
+
+ Date now = _quotaService.computeAdjustedTime(new Date());
+ if (endDate.compareTo(now) < 0) {
+ throw new InvalidParameterValueException(String.format("The quota tariff's end date [%s] cannot be less than now [%s].", endDate, now));
+ }
+
+ newQuotaTariff.setEndDate(endDate);
}
@Override
@@ -546,4 +618,39 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
return Date.from(nextDayLocalDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
-}
\ No newline at end of file
+ @Override
+ public QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd) {
+ String name = cmd.getName();
+ int usageType = cmd.getUsageType();
+ Date startDate = cmd.getStartDate();
+ Date now = new Date();
+ startDate = _quotaService.computeAdjustedTime(startDate == null ? now : startDate);
+ Date endDate = _quotaService.computeAdjustedTime(cmd.getEndDate());
+ Double value = cmd.getValue();
+ String description = cmd.getDescription();
+ String activationRule = cmd.getActivationRule();
+
+ QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name);
+
+ if (currentQuotaTariff != null) {
+ throw new InvalidParameterValueException(String.format("A quota tariff with name [%s] already exist.", name));
+ }
+
+ if (startDate.compareTo(now) < 0) {
+ throw new InvalidParameterValueException(String.format("The quota tariff's start date [%s] cannot be less than now [%s]", startDate, now));
+ }
+
+ return persistNewQuotaTariff(null, name, usageType, startDate, cmd.getEntityOwnerId(), endDate, value, description, activationRule);
+ }
+
+ public boolean deleteQuotaTariff(String quotaTariffUuid) {
+ QuotaTariffVO quotaTariff = _quotaTariffDao.findByUuid(quotaTariffUuid);
+
+ if (quotaTariff == null) {
+ throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Quota tariff with the provided UUID does not exist.");
+ }
+
+ quotaTariff.setRemoved(_quotaService.computeAdjustedTime(new Date()));
+ return _quotaTariffDao.updateQuotaTariff(quotaTariff);
+ }
+}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaTariffResponse.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaTariffResponse.java
index 48697fd2a82..ce4c5953641 100644
--- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaTariffResponse.java
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaTariffResponse.java
@@ -47,16 +47,40 @@ public class QuotaTariffResponse extends BaseResponse {
private BigDecimal tariffValue;
@SerializedName("effectiveDate")
- @Param(description = "the date on/after which this quota value will be effective")
+ @Param(description = "the start date of the quota tariff")
private Date effectiveOn = null;
+ @SerializedName("usageTypeDescription")
+ @Param(description = "usage type description")
+ private String usageTypeDescription;
+
+ @SerializedName("currency")
+ @Param(description = "currency")
+ private String currency;
+
+ @SerializedName("endDate")
+ @Param(description = "the end date of the quota tariff")
+ private Date endDate;
+
+ @SerializedName("activationRule")
+ @Param(description = "activation rule of the quota tariff")
+ private String activationRule;
+
+ @SerializedName("name")
+ @Param(description = "name")
+ private String name;
+
@SerializedName("description")
@Param(description = "description")
private String description;
- @SerializedName("currency")
- @Param(description = "currency")
- private String currency;
+ @SerializedName("uuid")
+ @Param(description = "uuid")
+ private String uuid;
+
+ @SerializedName("removed")
+ @Param(description = "when the quota tariff was removed")
+ private Date removed;
public QuotaTariffResponse() {
super();
@@ -108,12 +132,12 @@ public class QuotaTariffResponse extends BaseResponse {
this.tariffValue = tariffValue;
}
- public String getDescription() {
- return description;
+ public String getUsageTypeDescription() {
+ return usageTypeDescription;
}
- public void setDescription(String description) {
- this.description = description;
+ public void setUsageTypeDescription(String usageTypeDescription) {
+ this.usageTypeDescription = usageTypeDescription;
}
public Date getEffectiveOn() {
@@ -131,4 +155,53 @@ public class QuotaTariffResponse extends BaseResponse {
public void setCurrency(String currency) {
this.currency = currency;
}
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public String getActivationRule() {
+ return activationRule;
+ }
+
+ public void setActivationRule(String activationRule) {
+ this.activationRule = activationRule;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public Date getRemoved() {
+ return removed;
+ }
+
+ public void setRemoved(Date removed) {
+ this.removed = removed;
+ }
+
}
diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
index a8c28a53fe1..f0cf8833b85 100644
--- a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
+++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
@@ -34,6 +34,8 @@ import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaEnabledCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
+import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
+import org.apache.cloudstack.api.command.QuotaTariffDeleteCmd;
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
import org.apache.cloudstack.api.command.QuotaUpdateCmd;
@@ -126,6 +128,8 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
cmdList.add(QuotaCreditsCmd.class);
cmdList.add(QuotaEmailTemplateListCmd.class);
cmdList.add(QuotaEmailTemplateUpdateCmd.class);
+ cmdList.add(QuotaTariffCreateCmd.class);
+ cmdList.add(QuotaTariffDeleteCmd.class);
return cmdList;
}
@@ -137,7 +141,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
@Override
public ConfigKey>[] getConfigKeys() {
return new ConfigKey>[] {QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaStatementPeriod, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout,
- QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender, QuotaSmtpEnabledSecurityProtocols, QuotaSmtpUseStartTLS};
+ QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender, QuotaSmtpEnabledSecurityProtocols, QuotaSmtpUseStartTLS, QuotaActivationRuleTimeout};
}
@Override
@@ -248,6 +252,10 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
@Override
public Date computeAdjustedTime(final Date date) {
+ if (date == null) {
+ return null;
+ }
+
Calendar cal = Calendar.getInstance();
cal.setTime(date);
TimeZone localTZ = cal.getTimeZone();
diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java
index 1cbd575df37..e57109c4761 100644
--- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java
+++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffListCmdTest.java
@@ -52,7 +52,7 @@ public class QuotaTariffListCmdTest extends TestCase {
QuotaTariffVO tariff = new QuotaTariffVO();
tariff.setEffectiveOn(new Date());
tariff.setCurrencyValue(new BigDecimal(100));
- tariff.setUsageType(QuotaTypes.MEMORY);
+ tariff.setUsageType(QuotaTypes.VOLUME);
quotaTariffVOList.add(new QuotaTariffVO());
Mockito.when(responseBuilder.listQuotaTariffPlans(Mockito.eq(cmd))).thenReturn(new Pair<>(quotaTariffVOList, quotaTariffVOList.size()));
diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java
index dc50af1c28d..0cb1799ec10 100644
--- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java
+++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmdTest.java
@@ -50,7 +50,7 @@ public class QuotaTariffUpdateCmdTest extends TestCase {
QuotaTariffVO tariff = new QuotaTariffVO();
tariff.setEffectiveOn(new Date());
tariff.setCurrencyValue(new BigDecimal(100));
- tariff.setUsageType(QuotaTypes.MEMORY);
+ tariff.setUsageType(QuotaTypes.VOLUME);
Mockito.when(responseBuilder.updateQuotaTariffPlan(Mockito.eq(cmd))).thenReturn(null);
try {
diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
index fa4d9ab3058..2c27b8ef8a5 100644
--- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
+++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
@@ -16,7 +16,6 @@
// under the License.
package org.apache.cloudstack.api.response;
-import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -24,9 +23,9 @@ import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.function.Consumer;
-import javax.inject.Inject;
-
+import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.quota.QuotaService;
@@ -39,84 +38,59 @@ 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.commons.lang3.time.DateUtils;
import org.junit.Assert;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.runners.MockitoJUnitRunner;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
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;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(PowerMockRunner.class)
public class QuotaResponseBuilderImplTest extends TestCase {
@Mock
- QuotaTariffDao quotaTariffDao;
- @Mock
- QuotaBalanceDao quotaBalanceDao;
- @Mock
- QuotaCreditsDao quotaCreditsDao;
- @Mock
- QuotaEmailTemplatesDao quotaEmailTemplateDao;
+ QuotaTariffDao quotaTariffDaoMock;
@Mock
- UserDao userDao;
+ QuotaBalanceDao quotaBalanceDaoMock;
+
@Mock
- QuotaService quotaService;
+ QuotaCreditsDao quotaCreditsDaoMock;
+
@Mock
- AccountDao accountDao;
- @Inject
- AccountManager accountMgr;
+ QuotaEmailTemplatesDao quotaEmailTemplateDaoMock;
- QuotaResponseBuilderImpl quotaResponseBuilder = new QuotaResponseBuilderImpl();
+ @Mock
+ UserDao userDaoMock;
- @Before
- public void setup() throws IllegalAccessException, NoSuchFieldException {
- // Dummy transaction stack setup
- TransactionLegacy.open("QuotaResponseBuilderImplTest");
+ @Mock
+ QuotaService quotaServiceMock;
- Field tariffDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaTariffDao");
- tariffDaoField.setAccessible(true);
- tariffDaoField.set(quotaResponseBuilder, quotaTariffDao);
+ @Mock
+ AccountDao accountDaoMock;
- Field balanceDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaBalanceDao");
- balanceDaoField.setAccessible(true);
- balanceDaoField.set(quotaResponseBuilder, quotaBalanceDao);
+ @Mock
+ Consumer consumerStringMock;
- Field quotaCreditsDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaCreditsDao");
- quotaCreditsDaoField.setAccessible(true);
- quotaCreditsDaoField.set(quotaResponseBuilder, quotaCreditsDao);
+ @Mock
+ QuotaTariffVO quotaTariffVoMock;
- Field quotaEmailTemplateDaoField = QuotaResponseBuilderImpl.class.getDeclaredField("_quotaEmailTemplateDao");
- quotaEmailTemplateDaoField.setAccessible(true);
- quotaEmailTemplateDaoField.set(quotaResponseBuilder, quotaEmailTemplateDao);
+ @InjectMocks
+ QuotaResponseBuilderImpl quotaResponseBuilderSpy = Mockito.spy(QuotaResponseBuilderImpl.class);
- 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("_accountMgr");
- regionMgrField.setAccessible(true);
- regionMgrField.set(quotaResponseBuilder, accountMgr);
- }
+ Date date = new Date();
private QuotaTariffVO makeTariffTestData() {
QuotaTariffVO tariffVO = new QuotaTariffVO();
@@ -132,7 +106,7 @@ public class QuotaResponseBuilderImplTest extends TestCase {
@Test
public void testQuotaResponse() {
QuotaTariffVO tariffVO = makeTariffTestData();
- QuotaTariffResponse response = quotaResponseBuilder.createQuotaTariffResponse(tariffVO);
+ QuotaTariffResponse response = quotaResponseBuilderSpy.createQuotaTariffResponse(tariffVO);
assertTrue(tariffVO.getUsageType() == response.getUsageType());
assertTrue(tariffVO.getCurrencyValue().equals(response.getTariffValue()));
}
@@ -147,15 +121,15 @@ public class QuotaResponseBuilderImplTest extends TestCase {
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));
- Mockito.when(quotaService.computeAdjustedTime(Mockito.any(Date.class))).thenReturn(new Date());
+ Mockito.when(quotaCreditsDaoMock.saveCredits(Mockito.any(QuotaCreditsVO.class))).thenReturn(credit);
+ Mockito.when(quotaBalanceDaoMock.lastQuotaBalance(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(Date.class))).thenReturn(new BigDecimal(111));
+ Mockito.when(quotaServiceMock.computeAdjustedTime(Mockito.any(Date.class))).thenReturn(new Date());
AccountVO account = new AccountVO();
account.setState(Account.State.LOCKED);
- Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(account);
+ Mockito.when(accountDaoMock.findById(Mockito.anyLong())).thenReturn(account);
- QuotaCreditsResponse resp = quotaResponseBuilder.addQuotaCredits(accountId, domainId, amount, updatedBy, true);
+ QuotaCreditsResponse resp = quotaResponseBuilderSpy.addQuotaCredits(accountId, domainId, amount, updatedBy, true);
assertTrue(resp.getCredits().compareTo(credit.getCredit()) == 0);
}
@@ -167,9 +141,9 @@ public class QuotaResponseBuilderImplTest extends TestCase {
QuotaEmailTemplatesVO template = new QuotaEmailTemplatesVO();
template.setTemplateName("template");
templates.add(template);
- Mockito.when(quotaEmailTemplateDao.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(templates);
+ Mockito.when(quotaEmailTemplateDaoMock.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(templates);
- assertTrue(quotaResponseBuilder.listQuotaEmailTemplates(cmd).size() == 1);
+ Assert.assertEquals(1, quotaResponseBuilderSpy.listQuotaEmailTemplates(cmd).size());
}
@Test
@@ -181,17 +155,17 @@ public class QuotaResponseBuilderImplTest extends TestCase {
List templates = new ArrayList<>();
- Mockito.when(quotaEmailTemplateDao.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(templates);
- Mockito.when(quotaEmailTemplateDao.updateQuotaEmailTemplate(Mockito.any(QuotaEmailTemplatesVO.class))).thenReturn(true);
+ Mockito.when(quotaEmailTemplateDaoMock.listAllQuotaEmailTemplates(Mockito.anyString())).thenReturn(templates);
+ Mockito.when(quotaEmailTemplateDaoMock.updateQuotaEmailTemplate(Mockito.any(QuotaEmailTemplatesVO.class))).thenReturn(true);
// invalid template test
- assertFalse(quotaResponseBuilder.updateQuotaEmailTemplate(cmd));
+ assertFalse(quotaResponseBuilderSpy.updateQuotaEmailTemplate(cmd));
// valid template test
QuotaEmailTemplatesVO template = new QuotaEmailTemplatesVO();
template.setTemplateName("template");
templates.add(template);
- assertTrue(quotaResponseBuilder.updateQuotaEmailTemplate(cmd));
+ assertTrue(quotaResponseBuilderSpy.updateQuotaEmailTemplate(cmd));
}
@Test
@@ -199,14 +173,14 @@ public class QuotaResponseBuilderImplTest extends TestCase {
List quotaBalance = new ArrayList<>();
// null balance test
try {
- quotaResponseBuilder.createQuotaLastBalanceResponse(null, new Date());
+ quotaResponseBuilderSpy.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());
+ quotaResponseBuilderSpy.createQuotaLastBalanceResponse(quotaBalance, new Date());
} catch (InvalidParameterValueException e) {
assertTrue(e.getMessage().equals("There are no balance entries on or before the requested date."));
}
@@ -217,14 +191,14 @@ public class QuotaResponseBuilderImplTest extends TestCase {
entry.setCreditBalance(new BigDecimal(100));
quotaBalance.add(entry);
quotaBalance.add(entry);
- Mockito.lenient().when(quotaService.computeAdjustedTime(Mockito.any(Date.class))).thenReturn(new Date());
- QuotaBalanceResponse resp = quotaResponseBuilder.createQuotaLastBalanceResponse(quotaBalance, null);
+ Mockito.lenient().when(quotaServiceMock.computeAdjustedTime(Mockito.any(Date.class))).thenReturn(new Date());
+ QuotaBalanceResponse resp = quotaResponseBuilderSpy.createQuotaLastBalanceResponse(quotaBalance, null);
assertTrue(resp.getStartQuota().compareTo(new BigDecimal(200)) == 0);
}
@Test
public void testStartOfNextDayWithoutParameters() {
- Date nextDate = quotaResponseBuilder.startOfNextDay();
+ Date nextDate = quotaResponseBuilderSpy.startOfNextDay();
LocalDateTime tomorrowAtStartOfTheDay = LocalDate.now().atStartOfDay().plusDays(1);
Date expectedNextDate = Date.from(tomorrowAtStartOfTheDay.atZone(ZoneId.systemDefault()).toInstant());
@@ -236,11 +210,133 @@ public class QuotaResponseBuilderImplTest extends TestCase {
public void testStartOfNextDayWithParameter() {
Date anyDate = new Date(1242421545757532l);
- Date nextDayDate = quotaResponseBuilder.startOfNextDay(anyDate);
+ Date nextDayDate = quotaResponseBuilderSpy.startOfNextDay(anyDate);
LocalDateTime nextDayLocalDateTimeAtStartOfTheDay = anyDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().plusDays(1).atStartOfDay();
Date expectedNextDate = Date.from(nextDayLocalDateTimeAtStartOfTheDay.atZone(ZoneId.systemDefault()).toInstant());
Assert.assertEquals(expectedNextDate, nextDayDate);
}
+
+ @Test
+ public void validateStringsOnCreatingNewQuotaTariffTestNullValueDoNothing() {
+ quotaResponseBuilderSpy.validateStringsOnCreatingNewQuotaTariff(consumerStringMock, null);
+ Mockito.verify(consumerStringMock, Mockito.never()).accept(Mockito.anyString());
+ }
+
+ @Test
+ public void validateStringsOnCreatingNewQuotaTariffTestEmptyValueCallMethodWithNull() {
+ quotaResponseBuilderSpy.validateStringsOnCreatingNewQuotaTariff(consumerStringMock, "");
+ Mockito.verify(consumerStringMock).accept(null);
+ }
+
+ @Test
+ public void validateStringsOnCreatingNewQuotaTariffTestValueCallMethodWithValue() {
+ String value = "test";
+ quotaResponseBuilderSpy.validateStringsOnCreatingNewQuotaTariff(consumerStringMock, value);
+ Mockito.verify(consumerStringMock).accept(value);
+ }
+
+ @Test
+ public void validateValueOnCreatingNewQuotaTariffTestNullValueDoNothing() {
+ quotaResponseBuilderSpy.validateValueOnCreatingNewQuotaTariff(quotaTariffVoMock, null);
+ Mockito.verify(quotaTariffVoMock, Mockito.never()).setCurrencyValue(Mockito.any(BigDecimal.class));
+ }
+
+ @Test
+ public void validateValueOnCreatingNewQuotaTariffTestAnyValueIsSet() {
+ Double value = 0.0;
+ quotaResponseBuilderSpy.validateValueOnCreatingNewQuotaTariff(quotaTariffVoMock, value);
+ Mockito.verify(quotaTariffVoMock).setCurrencyValue(BigDecimal.valueOf(value));
+ }
+
+ @Test
+ public void validateEndDateOnCreatingNewQuotaTariffTestNullEndDateDoNothing() {
+ Date startDate = null;
+ Date endDate = null;
+
+ quotaResponseBuilderSpy.validateEndDateOnCreatingNewQuotaTariff(quotaTariffVoMock, startDate, endDate);
+ Mockito.verify(quotaTariffVoMock, Mockito.never()).setEndDate(Mockito.any(Date.class));
+ }
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void validateEndDateOnCreatingNewQuotaTariffTestEndDateLessThanStartDateThrowInvalidParameterValueException() {
+ Date startDate = date;
+ Date endDate = DateUtils.addSeconds(startDate, -1);
+
+ quotaResponseBuilderSpy.validateEndDateOnCreatingNewQuotaTariff(quotaTariffVoMock, startDate, endDate);
+ }
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void validateEndDateOnCreatingNewQuotaTariffTestEndDateLessThanNowThrowInvalidParameterValueException() {
+ Date startDate = DateUtils.addDays(date, -100);
+ Date endDate = DateUtils.addDays(new Date(), -1);
+
+ Mockito.doReturn(date).when(quotaServiceMock).computeAdjustedTime(Mockito.any(Date.class));
+ quotaResponseBuilderSpy.validateEndDateOnCreatingNewQuotaTariff(quotaTariffVoMock, startDate, endDate);
+ }
+
+ @Test
+ public void validateEndDateOnCreatingNewQuotaTariffTestSetValidEndDate() {
+ Date startDate = DateUtils.addDays(date, -100);
+ Date endDate = date;
+
+ Mockito.doReturn(DateUtils.addDays(date, -10)).when(quotaServiceMock).computeAdjustedTime(Mockito.any(Date.class));
+ quotaResponseBuilderSpy.validateEndDateOnCreatingNewQuotaTariff(quotaTariffVoMock, startDate, endDate);
+ Mockito.verify(quotaTariffVoMock).setEndDate(Mockito.any(Date.class));
+ }
+
+ @Test
+ @PrepareForTest(QuotaResponseBuilderImpl.class)
+ public void getNewQuotaTariffObjectTestCreateFromCurrentQuotaTariff() throws Exception {
+ PowerMockito.whenNew(QuotaTariffVO.class).withArguments(Mockito.any(QuotaTariffVO.class)).thenReturn(quotaTariffVoMock);
+
+ quotaResponseBuilderSpy.getNewQuotaTariffObject(quotaTariffVoMock, "", 0);
+ PowerMockito.verifyNew(QuotaTariffVO.class).withArguments(Mockito.any(QuotaTariffVO.class));
+ }
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void getNewQuotaTariffObjectTestSetInvalidUsageTypeThrowsInvalidParameterValueException() throws InvalidParameterValueException {
+ quotaResponseBuilderSpy.getNewQuotaTariffObject(null, "test", 0);
+ }
+
+ @Test
+ public void getNewQuotaTariffObjectTestReturnValidObject() throws InvalidParameterValueException {
+ String name = "test";
+ int usageType = 1;
+ QuotaTariffVO result = quotaResponseBuilderSpy.getNewQuotaTariffObject(null, name, usageType);
+
+ Assert.assertEquals(name, result.getName());
+ Assert.assertEquals(usageType, result.getUsageType());
+ }
+
+ @Test
+ public void persistNewQuotaTariffTestpersistNewQuotaTariff() {
+ Mockito.doReturn(quotaTariffVoMock).when(quotaResponseBuilderSpy).getNewQuotaTariffObject(Mockito.any(QuotaTariffVO.class), Mockito.anyString(), Mockito.anyInt());
+ Mockito.doNothing().when(quotaResponseBuilderSpy).validateEndDateOnCreatingNewQuotaTariff(Mockito.any(QuotaTariffVO.class), Mockito.any(Date.class), Mockito.any(Date.class));
+ Mockito.doNothing().when(quotaResponseBuilderSpy).validateValueOnCreatingNewQuotaTariff(Mockito.any(QuotaTariffVO.class), Mockito.anyDouble());
+ Mockito.doNothing().when(quotaResponseBuilderSpy).validateStringsOnCreatingNewQuotaTariff(Mockito.any(Consumer.class), Mockito.anyString());
+ Mockito.doReturn(quotaTariffVoMock).when(quotaTariffDaoMock).addQuotaTariff(Mockito.any(QuotaTariffVO.class));
+
+ quotaResponseBuilderSpy.persistNewQuotaTariff(quotaTariffVoMock, "", 1, date, 1l, date, 1.0, "", "");
+
+ Mockito.verify(quotaTariffDaoMock).addQuotaTariff(Mockito.any(QuotaTariffVO.class));
+ }
+
+ @Test (expected = ServerApiException.class)
+ public void deleteQuotaTariffTestQuotaDoesNotExistThrowsServerApiException() {
+ Mockito.doReturn(null).when(quotaTariffDaoMock).findById(Mockito.anyLong());
+ quotaResponseBuilderSpy.deleteQuotaTariff("");
+ }
+
+ @Test
+ public void deleteQuotaTariffTestUpdateRemoved() {
+ Mockito.doReturn(quotaTariffVoMock).when(quotaTariffDaoMock).findByUuid(Mockito.anyString());
+ Mockito.doReturn(true).when(quotaTariffDaoMock).updateQuotaTariff(Mockito.any(QuotaTariffVO.class));
+ Mockito.doReturn(new Date()).when(quotaServiceMock).computeAdjustedTime(Mockito.any(Date.class));
+
+ Assert.assertTrue(quotaResponseBuilderSpy.deleteQuotaTariff(""));
+
+ Mockito.verify(quotaTariffVoMock).setRemoved(Mockito.any(Date.class));
+ }
}
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
index 2a4e204ffd8..a265f661f49 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
@@ -44,6 +44,7 @@ import org.apache.cloudstack.agent.directdownload.HttpDirectDownloadCommand;
import org.apache.cloudstack.agent.directdownload.HttpsDirectDownloadCommand;
import org.apache.cloudstack.agent.directdownload.MetalinkDirectDownloadCommand;
import org.apache.cloudstack.agent.directdownload.NfsDirectDownloadCommand;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.storage.command.AttachAnswer;
import org.apache.cloudstack.storage.command.AttachCommand;
import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand;
@@ -118,7 +119,6 @@ import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StorageLayer;
import com.cloud.storage.resource.StorageProcessor;
-import static com.cloud.storage.snapshot.SnapshotManager.BackupSnapshotAfterTakingSnapshot;
import com.cloud.storage.template.Processor;
import com.cloud.storage.template.Processor.FormatInfo;
import com.cloud.storage.template.QCOW2Processor;
@@ -1046,7 +1046,7 @@ public class KVMStorageProcessor implements StorageProcessor {
s_logger.debug("Ignoring removal of vm snapshot on primary as this snapshot is created from vm snapshot");
} else if (primaryPool.getType() != StoragePoolType.RBD) {
String snapshotPath = snapshot.getPath();
- String backupSnapshotAfterTakingSnapshot = cmd.getOptions() == null ? null : cmd.getOptions().get(BackupSnapshotAfterTakingSnapshot.key());
+ String backupSnapshotAfterTakingSnapshot = cmd.getOptions() == null ? null : cmd.getOptions().get(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key());
if (backupSnapshotAfterTakingSnapshot == null || BooleanUtils.toBoolean(backupSnapshotAfterTakingSnapshot)) {
try {
diff --git a/server/src/main/java/com/cloud/api/ApiServer.java b/server/src/main/java/com/cloud/api/ApiServer.java
index 76b592a9d90..136cd624325 100644
--- a/server/src/main/java/com/cloud/api/ApiServer.java
+++ b/server/src/main/java/com/cloud/api/ApiServer.java
@@ -531,7 +531,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
final String key = (String)keysIter.next();
final String[] value = (String[])params.get(key);
// fail if parameter value contains ASCII control (non-printable) characters
- if (value[0] != null) {
+ if (value[0] != null && !ApiConstants.ACTIVATION_RULE.equals(key)) {
final Pattern pattern = Pattern.compile(CONTROL_CHARACTERS);
final Matcher matcher = pattern.matcher(value[0]);
if (matcher.find()) {
diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java
index 1012deba16f..7219e0dbb6f 100644
--- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java
+++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java
@@ -56,9 +56,6 @@ public interface SnapshotManager extends Configurable {
public static final ConfigKey BackupRetryInterval = new ConfigKey(Integer.class, "backup.retry.interval", "Advanced", "300",
"Time in seconds between retries in backing up snapshot to secondary", false, ConfigKey.Scope.Global, null);
- public static final ConfigKey BackupSnapshotAfterTakingSnapshot = new ConfigKey(Boolean.class, "snapshot.backup.to.secondary", "Snapshots", "true",
- "Indicates whether to always backup primary storage snapshot to secondary storage. Keeping snapshots only on Primary storage is applicable for KVM + Ceph only.", false, ConfigKey.Scope.Global, null);
-
public static final ConfigKey VmStorageSnapshotKvm = new ConfigKey<>(Boolean.class, "kvm.vmstoragesnapshot.enabled", "Snapshots", "false", "For live snapshot of virtual machine instance on KVM hypervisor without memory. Requieres qemu version 1.6+ (on NFS or Local file system) and qemu-guest-agent installed on guest VM", true, ConfigKey.Scope.Global, null);
void deletePoliciesForVolume(Long volumeId);
diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java
index 4dbfc6f97df..6a7006e8188 100755
--- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java
@@ -231,7 +231,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
@Override
public ConfigKey>[] getConfigKeys() {
return new ConfigKey>[] {BackupRetryAttempts, BackupRetryInterval, SnapshotHourlyMax, SnapshotDailyMax, SnapshotMonthlyMax, SnapshotWeeklyMax, usageSnapshotSelection,
- BackupSnapshotAfterTakingSnapshot, VmStorageSnapshotKvm};
+ SnapshotInfo.BackupSnapshotAfterTakingSnapshot, VmStorageSnapshotKvm};
}
@Override
@@ -1250,7 +1250,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
SnapshotInfo snapshotOnPrimary = snapshotStrategy.takeSnapshot(snapshot);
- boolean backupSnapToSecondary = BackupSnapshotAfterTakingSnapshot.value() == null || BackupSnapshotAfterTakingSnapshot.value();
+ boolean backupSnapToSecondary = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value() == null || SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
if (backupSnapToSecondary) {
backupSnapshotToSecondary(payload.getAsyncBackup(), snapshotStrategy, snapshotOnPrimary);
diff --git a/server/src/main/java/org/apache/cloudstack/snapshot/SnapshotHelper.java b/server/src/main/java/org/apache/cloudstack/snapshot/SnapshotHelper.java
index dc4b17cfc06..b00612dca65 100644
--- a/server/src/main/java/org/apache/cloudstack/snapshot/SnapshotHelper.java
+++ b/server/src/main/java/org/apache/cloudstack/snapshot/SnapshotHelper.java
@@ -28,7 +28,6 @@ import com.cloud.storage.VolumeVO;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.dao.SnapshotDao;
-import static com.cloud.storage.snapshot.SnapshotManager.BackupSnapshotAfterTakingSnapshot;
import com.cloud.utils.exception.CloudRuntimeException;
import java.util.Arrays;
@@ -82,7 +81,7 @@ public class SnapshotHelper {
@Inject
protected PrimaryDataStoreDao primaryDataStoreDao;
- protected boolean backupSnapshotAfterTakingSnapshot = BackupSnapshotAfterTakingSnapshot.value();
+ protected boolean backupSnapshotAfterTakingSnapshot = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
protected final Set storagePoolTypesToValidateWithBackupSnapshotAfterTakingSnapshot = new HashSet<>(Arrays.asList(StoragePoolType.RBD,
StoragePoolType.PowerFlex));
@@ -103,7 +102,7 @@ public class SnapshotHelper {
}
logger.debug(String.format("Expunging snapshot [%s] due to it is a temporary backup to create a volume from snapshot. It is occurring because the global setting [%s]"
- + " has the value [%s].", snapInfo.getId(), BackupSnapshotAfterTakingSnapshot.key(), backupSnapshotAfterTakingSnapshot));
+ + " has the value [%s].", snapInfo.getId(), SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(), backupSnapshotAfterTakingSnapshot));
try {
snapshotService.deleteSnapshot(snapInfo);
diff --git a/server/src/test/java/com/cloud/user/MockUsageEventDao.java b/server/src/test/java/com/cloud/user/MockUsageEventDao.java
index 5d8c62115ec..029625d3a8e 100644
--- a/server/src/test/java/com/cloud/user/MockUsageEventDao.java
+++ b/server/src/test/java/com/cloud/user/MockUsageEventDao.java
@@ -318,4 +318,9 @@ public class MockUsageEventDao implements UsageEventDao{
public void saveDetails(long eventId, Map details) {
}
+
+ @Override
+ public Pair, Integer> searchAndCount(SearchCriteria sc, Filter filter, boolean includeRemoved) {
+ return null;
+ }
}
diff --git a/usage/src/main/resources/usageApplicationContext.xml b/usage/src/main/resources/usageApplicationContext.xml
index f44f08b29d7..724e6ae4425 100644
--- a/usage/src/main/resources/usageApplicationContext.xml
+++ b/usage/src/main/resources/usageApplicationContext.xml
@@ -29,11 +29,17 @@
http://www.springframework.org/schema/context/spring-context.xsd">
+
+
+
+
+
+
diff --git a/utils/pom.xml b/utils/pom.xml
index e1b24918f4e..db219b080f7 100755
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -205,6 +205,11 @@
commons-email1.5
+
+ org.openjdk.nashorn
+ nashorn-core
+ 15.3
+
diff --git a/utils/src/main/java/com/cloud/utils/DateUtil.java b/utils/src/main/java/com/cloud/utils/DateUtil.java
index 8c55268782e..4d0157f0e0f 100644
--- a/utils/src/main/java/com/cloud/utils/DateUtil.java
+++ b/utils/src/main/java/com/cloud/utils/DateUtil.java
@@ -33,6 +33,8 @@ import java.time.OffsetDateTime;
import com.cloud.utils.exception.CloudRuntimeException;
public class DateUtil {
+ public static final int HOURS_IN_A_MONTH = 30 * 24;
+
public static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
public static final String YYYYMMDD_FORMAT = "yyyyMMddHHmmss";
private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
diff --git a/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java
index 95d455f1247..13f565f19fc 100644
--- a/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java
+++ b/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java
@@ -22,6 +22,7 @@ public class ByteScaleUtils {
public static final long KiB = 1024;
public static final long MiB = KiB * 1024;
+ public static final long GiB = MiB * 1024;
private ByteScaleUtils() {}
@@ -44,4 +45,14 @@ public class ByteScaleUtils {
public static long bytesToKib(long b) {
return b / KiB;
}
+
+ /**
+ * Converts bytes to mebibytes.
+ *
+ * @param b The value in bytes to convert to mebibytes.
+ * @return The parameter divided by 1024 * 1024 (1 MiB).
+ */
+ public static long bytesToMib(long b) {
+ return b / MiB;
+ }
}
diff --git a/utils/src/main/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreter.java b/utils/src/main/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreter.java
new file mode 100644
index 00000000000..03ad3b401b8
--- /dev/null
+++ b/utils/src/main/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreter.java
@@ -0,0 +1,146 @@
+// 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.utils.jsinterpreter;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
+
+import javax.script.ScriptEngine;
+
+/**
+ * A class to execute JavaScript scripts, with the possibility to inject context to the scripts.
+ */
+public class JsInterpreter implements Closeable {
+ protected Logger logger = Logger.getLogger(JsInterpreter.class);
+
+ protected ScriptEngine interpreter;
+ protected String interpreterName;
+ private final String injectingLogMessage = "Injecting variable [%s] with value [%s] into the JS interpreter.";
+ protected ExecutorService executor;
+ private TimeUnit defaultTimeUnit = TimeUnit.MILLISECONDS;
+ private long timeout;
+ private String timeoutDefaultMessage;
+ protected Map variables = new LinkedHashMap<>();
+
+ public JsInterpreter(long timeout) {
+ this.timeout = timeout;
+ this.timeoutDefaultMessage = String.format("Timeout (in milliseconds) defined in the global setting [quota.activationrule.timeout]: [%s].", this.timeout);
+
+ executor = Executors.newSingleThreadExecutor();
+ NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+
+ this.interpreterName = factory.getEngineName();
+ logger.trace(String.format("Initiating JS interpreter: %s.", interpreterName));
+
+ setScriptEngineDisablingJavaLanguage(factory);
+ }
+
+ protected void setScriptEngineDisablingJavaLanguage(NashornScriptEngineFactory factory) {
+ interpreter = factory.getScriptEngine("--no-java");
+ }
+
+ /**
+ * Discards the current variables map and create a new one.
+ */
+ public void discardCurrentVariables() {
+ logger.trace("Discarding current variables map and creating a new one.");
+ variables = new LinkedHashMap<>();
+ }
+
+ /**
+ * Adds the parameters to a Map that will be converted to JS variables right before executing the scripts..
+ * @param key The name of the variable.
+ * @param value The value of the variable.
+ */
+ public void injectVariable(String key, String value) {
+ logger.trace(String.format(injectingLogMessage, key, value));
+ variables.put(key, value);
+ }
+
+ /**
+ * Injects the variables to the script and execute it.
+ * @param script Code to be executed.
+ * @return The result of the executed script.
+ */
+ public Object executeScript(String script) {
+ script = addVariablesToScript(script);
+
+ logger.debug(String.format("Executing script [%s].", script));
+
+ Object result = executeScriptInThread(script);
+
+ logger.debug(String.format("The script [%s] had the following result: [%s].", script, result));
+ return result;
+ }
+
+ protected Object executeScriptInThread(String script) {
+ Callable