From 2ca164ac96b52b10694451236c3fb4826403ef6f Mon Sep 17 00:00:00 2001 From: Daniel Augusto Veronezi Salvador <38945620+GutoVeronezi@users.noreply.github.com> Date: Mon, 17 Oct 2022 05:03:50 -0300 Subject: [PATCH] Quota custom tariffs (#5909) Co-authored-by: GutoVeronezi Co-authored-by: dahn --- .../apache/cloudstack/api/ApiConstants.java | 1 + .../cloudstack/usage/UsageUnitTypes.java | 52 + .../cloudstack/usage/UsageUnitTypesTest.java | 53 + .../subsystem/api/storage/SnapshotInfo.java | 6 + .../cloud/vm/VirtualMachineManagerImpl.java | 2 - .../orchestration/VolumeOrchestrator.java | 3 +- .../upgrade/dao/Upgrade41610to41700.java | 1 + .../upgrade/dao/Upgrade41700to41800.java | 233 ++++ .../main/java/com/cloud/usage/UsageVO.java | 6 + .../java/com/cloud/usage/dao/UsageDao.java | 5 +- .../com/cloud/usage/dao/UsageDaoImpl.java | 86 +- .../main/java/com/cloud/user/AccountVO.java | 4 + .../com/cloud/vm/constants/VmDetails.java | 32 + .../db/SnapshotDataStoreDaoImpl.java | 4 +- ...s-between-management-and-usage-context.xml | 70 + ...spring-engine-schema-core-daos-context.xml | 41 +- .../META-INF/db/schema-41700to41800.sql | 88 ++ .../motion/AncientDataMotionStrategy.java | 3 +- .../test/resources/fakeDriverTestContext.xml | 2 +- .../src/test/resources/storageContext.xml | 2 +- .../db/SnapshotDataStoreDaoImplTest.java | 3 +- .../java/com/cloud/utils/db/GenericDao.java | 2 + .../com/cloud/utils/db/GenericDaoBase.java | 41 +- .../com/cloud/utils/db/SearchCriteria.java | 8 + .../cloud/utils/db/GenericDaoBaseTest.java | 32 + .../cloudstack/quota/QuotaManagerImpl.java | 663 +++++----- .../presetvariables/Account.java | 32 + .../presetvariables/BackupOffering.java | 33 + .../presetvariables/ComputeOffering.java | 32 + .../presetvariables/ComputingResources.java | 56 + .../presetvariables/Domain.java | 32 + .../GenericPresetVariable.java | 52 + .../activationrule/presetvariables/Host.java | 34 + .../presetvariables/PresetVariableHelper.java | 653 ++++++++++ .../presetvariables/PresetVariables.java | 76 ++ .../presetvariables/Resource.java | 48 + .../activationrule/presetvariables/Role.java | 34 + .../presetvariables/Storage.java | 46 + .../activationrule/presetvariables/Value.java | 188 +++ .../quota/constant/QuotaConfig.java | 3 + .../cloudstack/quota/constant/QuotaTypes.java | 52 +- .../cloudstack/quota/dao/QuotaTariffDao.java | 4 + .../quota/dao/QuotaTariffDaoImpl.java | 77 +- .../cloudstack/quota/dao/VmTemplateDao.java | 24 + .../quota/dao/VmTemplateDaoImpl.java | 33 + .../cloudstack/quota/vo/QuotaTariffVO.java | 98 +- .../quota/spring-framework-quota-context.xml | 1 + .../quota/QuotaManagerImplTest.java | 596 ++++++--- .../presetvariables/AccountTest.java | 34 + .../presetvariables/BackupOfferingTest.java | 36 + .../presetvariables/ComputeOfferingTest.java | 35 + .../ComputingResourcesTest.java | 40 + .../presetvariables/DomainTest.java | 35 + .../GenericPresetVariableTest.java | 73 ++ .../presetvariables/HostTest.java | 34 + .../PresetVariableHelperTest.java | 1151 +++++++++++++++++ .../presetvariables/ResourceTest.java | 40 + .../presetvariables/RoleTest.java | 34 + .../presetvariables/StorageTest.java | 41 + .../presetvariables/ValueTest.java | 139 ++ .../quota/constant/QuotaTypesTest.java | 2 +- .../quota/vo/QuotaTariffVOTest.java | 51 + .../api/command/QuotaTariffCreateCmd.java | 153 +++ .../api/command/QuotaTariffDeleteCmd.java | 77 ++ .../api/command/QuotaTariffListCmd.java | 33 +- .../api/command/QuotaTariffUpdateCmd.java | 44 +- .../api/response/QuotaResponseBuilder.java | 5 + .../response/QuotaResponseBuilderImpl.java | 197 ++- .../api/response/QuotaTariffResponse.java | 89 +- .../cloudstack/quota/QuotaServiceImpl.java | 10 +- .../api/command/QuotaTariffListCmdTest.java | 2 +- .../api/command/QuotaTariffUpdateCmdTest.java | 2 +- .../QuotaResponseBuilderImplTest.java | 238 +++- .../kvm/storage/KVMStorageProcessor.java | 4 +- .../main/java/com/cloud/api/ApiServer.java | 2 +- .../storage/snapshot/SnapshotManager.java | 3 - .../storage/snapshot/SnapshotManagerImpl.java | 4 +- .../cloudstack/snapshot/SnapshotHelper.java | 5 +- .../com/cloud/user/MockUsageEventDao.java | 5 + .../resources/usageApplicationContext.xml | 6 + utils/pom.xml | 5 + .../main/java/com/cloud/utils/DateUtil.java | 2 + .../utils/bytescale/ByteScaleUtils.java | 11 + .../utils/jsinterpreter/JsInterpreter.java | 146 +++ .../ReflectionToStringBuilderUtils.java | 14 + .../java/com/cloud/utils/UuidUtilsTest.java | 20 +- .../utils/bytescale/ByteScaleUtilsTest.java | 9 +- .../jsinterpreter/JsInterpreterTest.java | 188 +++ .../ReflectionToStringBuilderUtilsTest.java | 20 + 89 files changed, 5970 insertions(+), 716 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/usage/UsageUnitTypes.java create mode 100644 api/src/test/java/org/apache/cloudstack/usage/UsageUnitTypesTest.java create mode 100644 engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41800.java create mode 100644 engine/schema/src/main/java/com/cloud/vm/constants/VmDetails.java rename engine/{storage/src/main/java/org/apache/cloudstack/storage/image => schema/src/main/java/org/apache/cloudstack/storage/datastore}/db/SnapshotDataStoreDaoImpl.java (99%) create mode 100644 engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml create mode 100644 engine/schema/src/main/resources/META-INF/db/schema-41700to41800.sql rename engine/storage/src/test/java/org/apache/cloudstack/storage/{image => datastore}/db/SnapshotDataStoreDaoImplTest.java (98%) create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelper.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Resource.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDao.java create mode 100644 framework/quota/src/main/java/org/apache/cloudstack/quota/dao/VmTemplateDaoImpl.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/AccountTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOfferingTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOfferingTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResourcesTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/DomainTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariableTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/HostTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableHelperTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ResourceTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/RoleTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/StorageTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/vo/QuotaTariffVOTest.java create mode 100644 plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java create mode 100644 plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffDeleteCmd.java create mode 100644 utils/src/main/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreter.java create mode 100644 utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java 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 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-email 1.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 task = () -> interpreter.eval(script); + + Future future = executor.submit(task); + + try { + return future.get(this.timeout, defaultTimeUnit); + } catch (TimeoutException | InterruptedException | ExecutionException e) { + String message = String.format("Unable to execute script [%s] due to [%s]", script, e.getMessage()); + + if (e instanceof TimeoutException) { + message = String.format("Execution of script [%s] took too long and timed out. %s", script, timeoutDefaultMessage); + } + + logger.error(message, e); + throw new CloudRuntimeException(message, e); + } finally { + future.cancel(true); + } + } + + protected String addVariablesToScript(String script) { + if (MapUtils.isEmpty(variables)) { + logger.trace(String.format("There is no variables to add to script [%s]. Returning.", script)); + return script; + } + + String variablesToString = ""; + for (Map.Entry variable : variables.entrySet()) { + variablesToString = String.format("%s %s = %s;", variablesToString, variable.getKey(), variable.getValue()); + } + + return String.format("%s %s", variablesToString, script); + } + + + @Override + public void close() throws IOException { + executor.shutdown(); + } +} diff --git a/utils/src/main/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtils.java index d78b84fbdd0..5046747c258 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtils.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtils.java @@ -129,6 +129,20 @@ public class ReflectionToStringBuilderUtils { .collect(Collectors.joining(multipleValuesSeparator == null ? DEFAULT_MULTIPLE_VALUES_SEPARATOR : multipleValuesSeparator))); } + + /** + * Similar to {@link ReflectionToStringBuilderUtils#reflectOnlySelectedFields(Object, ToStringStyle, String, String...)}, but reflecting the whole collection.

+ * This method must be called only to {@link Collection}, as it will reflect the objects contained in it.
+ * To reflect the Collection itself or other objects, see {@link ReflectionToStringBuilder}. + * @param object Collection to be reflected. + * @return If object is null or is not a Collection, returns null.
+ * If object is a Collection, returns a style formatted string containing the not null elements, else, returns the object as the style parameter.
+ */ + public static String reflectCollection(Object object){ + String[] excludeFields = null; + return reflectCollection(object, DEFAULT_STYLE, DEFAULT_MULTIPLE_VALUES_SEPARATOR, excludeFields); + } + /** * Verify if object is a Collection. * @param object diff --git a/utils/src/test/java/com/cloud/utils/UuidUtilsTest.java b/utils/src/test/java/com/cloud/utils/UuidUtilsTest.java index 366fff46e2d..6935c96df34 100644 --- a/utils/src/test/java/com/cloud/utils/UuidUtilsTest.java +++ b/utils/src/test/java/com/cloud/utils/UuidUtilsTest.java @@ -27,16 +27,26 @@ import org.junit.Test; public class UuidUtilsTest { @Test - public void isUuidTestPass() throws Exception { + public void isUuidTestAllLowerCaseReturnTrue() { String serviceUuid = "f81a9aa3-1f7d-466f-b04b-f2b101486bae"; - assertTrue(UuidUtils.isUuid(serviceUuid)); } @Test - public void isUuidTestFail() throws Exception { - String serviceUuid = "6fc6ce7-d503-4f95-9e68-c9cd281b13df"; + public void isUuidTestAllUpperCaseReturnTrue() { + String serviceUuid = "F81A9AA3-1F7D-466F-B04B-F2B101486BAE"; + assertTrue(UuidUtils.isUuid(serviceUuid)); + } + @Test + public void isUuidTestAlternatingCaseReturnTrue() { + String serviceUuid = "f81A9Aa3-1f7d-466F-B04b-f2b101486BaE"; + assertTrue(UuidUtils.isUuid(serviceUuid)); + } + + @Test + public void isUuidTestNotUuidReturnFalse() { + String serviceUuid = "6fc6ce7-d503-4f95-9e68-c9cd281b13df"; assertFalse(UuidUtils.isUuid(serviceUuid)); } -} \ No newline at end of file +} diff --git a/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java b/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java index b3487641b42..e644d207e9b 100644 --- a/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java +++ b/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java @@ -31,11 +31,18 @@ public class ByteScaleUtilsTest extends TestCase { @Test public void validateBytesToKib() { - long kib = 1024L * 3000L; + long kib = 3000L; long b = 1024 * kib; assertEquals(kib, ByteScaleUtils.bytesToKib(b)); } + @Test + public void validateBytesToMib() { + long mib = 3000L; + long b = 1024L * 1024L * mib; + assertEquals(mib, ByteScaleUtils.bytesToMib(b)); + } + @Test public void validateMibToBytesIfIntTimesIntThenMustExtrapolateIntMaxValue() { int mib = 3000; diff --git a/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java b/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java new file mode 100644 index 00000000000..1567d897fe6 --- /dev/null +++ b/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.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.utils.jsinterpreter; + +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.TimeoutException; + +import com.cloud.utils.exception.CloudRuntimeException; +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.openjdk.nashorn.api.scripting.NashornScriptEngineFactory; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import javax.script.ScriptEngine; + +@RunWith(PowerMockRunner.class) +@PowerMockIgnore({"javax.xml.*", "org.apache.xerces.*", "org.xml.*", "org.w3c.*"}) +@PrepareForTest(JsInterpreter.class) +public class JsInterpreterTest { + private long timeout = 2000; + + @InjectMocks + @Spy + JsInterpreter jsInterpreterSpy = new JsInterpreter(timeout); + + @Mock + ExecutorService executorMock; + + @Mock + Future futureObjectMock; + + @Test + public void closeTestShutdownExecutor() throws IOException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + jsInterpreterSpy.executor = executor; + + jsInterpreterSpy.close(); + Assert.assertTrue(executor.isShutdown()); + } + + @Test + public void addVariablesToScriptTestVariablesMapIsEmptyReturnScript() { + String script = "a + b"; + jsInterpreterSpy.variables = new LinkedHashMap<>(); + + String result = jsInterpreterSpy.addVariablesToScript(script); + + Assert.assertEquals(script, result); + } + + @Test + public void addVariablesToScriptTestVariablesMapIsNotEmptyInjectVariableToScript() { + String script = "a + b"; + String var1 = "{test: \"test\"}"; + jsInterpreterSpy.injectVariable("var1", var1); + jsInterpreterSpy.injectVariable("var2", var1); + + String expected = String.format(" var1 = %s; var2 = %s; %s", var1, var1, script); + + String result = jsInterpreterSpy.addVariablesToScript(script); + + Assert.assertEquals(expected, result); + } + + @Test + public void executeScriptTestReturnResultOfScriptExecution() { + String script = "5"; + Object expected = new Object(); + Mockito.doReturn(script).when(jsInterpreterSpy).addVariablesToScript(Mockito.anyString()); + Mockito.doReturn(expected).when(jsInterpreterSpy).executeScript(Mockito.anyString()); + + Object result = jsInterpreterSpy.executeScript(script); + + Assert.assertEquals(expected, result); + } + + @Test(expected = CloudRuntimeException.class) + public void executeScriptInThreadTestThreadThrowInterruptedException() throws InterruptedException, ExecutionException, TimeoutException { + Mockito.doReturn(futureObjectMock).when(executorMock).submit(Mockito.>any()); + Mockito.doThrow(InterruptedException.class).when(futureObjectMock).get(Mockito.anyLong(), Mockito.any()); + + jsInterpreterSpy.executeScriptInThread("a"); + Mockito.verify(futureObjectMock).cancel(true); + } + + @Test(expected = CloudRuntimeException.class) + public void executeScriptInThreadTestThreadThrowExecutionException() throws InterruptedException, ExecutionException, TimeoutException { + Mockito.doReturn(futureObjectMock).when(executorMock).submit(Mockito.>any()); + Mockito.doThrow(ExecutionException.class).when(futureObjectMock).get(Mockito.anyLong(), Mockito.any()); + + jsInterpreterSpy.executeScriptInThread("b"); + Mockito.verify(futureObjectMock).cancel(true); + } + + @Test(expected = CloudRuntimeException.class) + public void executeScriptInThreadTestThreadThrowTimeoutException() throws InterruptedException, ExecutionException, TimeoutException { + Mockito.doReturn(futureObjectMock).when(executorMock).submit(Mockito.>any()); + Mockito.doThrow(TimeoutException.class).when(futureObjectMock).get(Mockito.anyLong(), Mockito.any()); + + jsInterpreterSpy.executeScriptInThread("c"); + Mockito.verify(futureObjectMock).cancel(true); + } + + @Test + public void executeScriptInThreadTestReturnResultOfScriptExecution() throws InterruptedException, ExecutionException, TimeoutException { + Object expected = new Object(); + + Mockito.doReturn(futureObjectMock).when(executorMock).submit(Mockito.>any()); + Mockito.doReturn(expected).when(futureObjectMock).get(Mockito.anyLong(), Mockito.any()); + + Object result = jsInterpreterSpy.executeScriptInThread(""); + + Assert.assertEquals(expected, result); + Mockito.verify(futureObjectMock).cancel(true); + } + + @Test + public void injectVariableTestAddVariableToMap() { + Map variables = new LinkedHashMap<>(); + variables.put("a", "b"); + variables.put("b", null); + + jsInterpreterSpy.variables = new LinkedHashMap<>(); + + jsInterpreterSpy.injectVariable("a", "b"); + jsInterpreterSpy.injectVariable("b", null); + + variables.forEach((key, value) -> { + Assert.assertEquals(value, jsInterpreterSpy.variables.get(key)); + }); + } + + @Test + public void discardCurrentVariablesTestInstantiateNewMap() { + Map variables = new LinkedHashMap<>(); + variables.put("a", "b"); + variables.put("b", null); + + jsInterpreterSpy.variables = variables; + + jsInterpreterSpy.discardCurrentVariables(); + + Assert.assertEquals(0, jsInterpreterSpy.variables.size()); + } + + @Test + @PrepareForTest(NashornScriptEngineFactory.class) + public void setScriptEngineDisablingJavaLanguageTest() { + NashornScriptEngineFactory nashornScriptEngineFactoryMock = Mockito.mock(NashornScriptEngineFactory.class); + ScriptEngine scriptEngineMock = Mockito.mock(ScriptEngine.class); + + Mockito.doReturn(scriptEngineMock).when(nashornScriptEngineFactoryMock).getScriptEngine(Mockito.anyString()); + + jsInterpreterSpy.setScriptEngineDisablingJavaLanguage(nashornScriptEngineFactoryMock); + + Assert.assertEquals(scriptEngineMock, jsInterpreterSpy.interpreter); + Mockito.verify(nashornScriptEngineFactoryMock).getScriptEngine("--no-java"); + } +} diff --git a/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java b/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java index 8990b1f6cf2..9291188b5b5 100644 --- a/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java +++ b/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java @@ -53,6 +53,9 @@ public class ReflectionToStringBuilderUtilsTest extends TestCase { private String classToReflectRemovedField; private String[] classToReflectFieldsNamesArray; + private static final ToStringStyle DEFAULT_STYLE = ToStringStyle.JSON_STYLE; + private static final String DEFAULT_MULTIPLE_VALUES_SEPARATOR = ","; + @Before public void setup(){ classToReflect = String.class; @@ -329,4 +332,21 @@ public class ReflectionToStringBuilderUtilsTest extends TestCase { String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(new Object()); Assert.assertEquals(expectedResult, result); } + + @Test + public void reflectCollectionTestCallBaseReflectCollectionMethodWithDefaultParameters() { + String expected = "test"; + + PowerMockito.spy(ReflectionToStringBuilderUtils.class); + PowerMockito.when(ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expected); + + Object object = Mockito.mock(Object.class); + String result = ReflectionToStringBuilderUtils.reflectCollection(object); + + Assert.assertEquals(expected, result); + + PowerMockito.verifyStatic(ReflectionToStringBuilderUtils.class); + String[] excludeFields = null; + ReflectionToStringBuilderUtils.reflectCollection(object, DEFAULT_STYLE, DEFAULT_MULTIPLE_VALUES_SEPARATOR, excludeFields); + } }