From 66dc3a8de0f6fe88ad57f9a3c12103f0bc2d1bab Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Mon, 29 Aug 2022 18:11:24 -0300 Subject: [PATCH 1/9] Add quota plugin to accout/domain scope --- .../quota/constant/QuotaConfig.java | 3 + .../quota/dao/QuotaAccountDaoImpl.java | 15 +++- .../quota/dao/QuotaAccountDaoImplTest.java | 80 +++++++++++++++++++ .../api/response/QuotaSummaryResponse.java | 12 +++ .../cloudstack/quota/QuotaServiceImpl.java | 2 +- ui/src/config/section/plugin/quota.js | 5 +- 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 framework/quota/src/test/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImplTest.java 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 4cb855f4facc..bc235eef5a90 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 conection.", true); + ConfigKey QuotaAccountEnabled = new ConfigKey<>("Advanced", Boolean.class, "quota.account.enabled", "false", "Indicates whether Quota plugin is enabled or not for " + + "the account.", true, ConfigKey.Scope.Account); + enum QuotaEmailTemplateTypes { QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT } diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java index 23df0d43cbc1..b90b03b0a4b8 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java @@ -16,8 +16,10 @@ //under the License. package org.apache.cloudstack.quota.dao; +import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.quota.constant.QuotaConfig; import org.apache.cloudstack.quota.vo.QuotaAccountVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -36,7 +38,15 @@ public class QuotaAccountDaoImpl extends GenericDaoBase im @Override public List listAllQuotaAccount() { - return listAllQuotaAccount(null, null).first(); + List accountsWithQuotaEnabled = new ArrayList<>(); + for (QuotaAccountVO account : listAllQuotaAccount(null, null).first()) { + if (getQuotaAccountEnabled(account.getAccountId())) { + accountsWithQuotaEnabled.add(account); + continue; + } + s_logger.trace(String.format("Account [%s] has the quota plugin disabled. Thus, it will not receive quota emails.", account)); + } + return accountsWithQuotaEnabled; } @Override @@ -80,4 +90,7 @@ public Boolean doInTransaction(final TransactionStatus status) { }); } + public Boolean getQuotaAccountEnabled(Long accountId) { + return QuotaConfig.QuotaAccountEnabled.valueIn(accountId); + } } diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImplTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImplTest.java new file mode 100644 index 000000000000..19040168c10b --- /dev/null +++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImplTest.java @@ -0,0 +1,80 @@ +// 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.utils.Pair; +import org.apache.cloudstack.quota.vo.QuotaAccountVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class QuotaAccountDaoImplTest { + @Spy + QuotaAccountDaoImpl quotaAccountDaoImplSpy; + + @Test + public void listAllQuotaAccountTestShouldReturnNullWithAccountWithQuotaDisabled() { + QuotaAccountVO accountWithQuotaDisabled = new QuotaAccountVO(1L); + + List allQuotaAccounts = List.of(accountWithQuotaDisabled); + Pair,Integer> pair = new Pair<>(allQuotaAccounts, 1); + + Mockito.doReturn(pair).when(quotaAccountDaoImplSpy).listAllQuotaAccount(null, null); + Mockito.doReturn(false).when(quotaAccountDaoImplSpy).getQuotaAccountEnabled(accountWithQuotaDisabled.getAccountId()); + + int expected = quotaAccountDaoImplSpy.listAllQuotaAccount().size(); + Assert.assertEquals(0, expected); + } + + @Test + public void listAllQuotaAccountTestShouldReturnSizeOneWithAccountWithQuotaEnabled() { + QuotaAccountVO accountWithQuotaEnabled = new QuotaAccountVO(2L); + + List allQuotaAccounts = List.of(accountWithQuotaEnabled); + Pair,Integer> pair = new Pair<>(allQuotaAccounts, 1); + + Mockito.doReturn(pair).when(quotaAccountDaoImplSpy).listAllQuotaAccount(null, null); + Mockito.doReturn(true).when(quotaAccountDaoImplSpy).getQuotaAccountEnabled(accountWithQuotaEnabled.getAccountId()); + + int expected = quotaAccountDaoImplSpy.listAllQuotaAccount().size(); + Assert.assertEquals(1, expected); + } + + @Test + public void listAllQuotaAccountTestShouldReturnOnlyAccountsWithQuotaEnabled() { + QuotaAccountVO accountWithQuotaEnabled = new QuotaAccountVO(1L); + QuotaAccountVO accountWithQuotaDisabled = new QuotaAccountVO(2L); + + List allQuotaAccounts = List.of(accountWithQuotaEnabled, accountWithQuotaDisabled); + Pair,Integer> pair = new Pair<>(allQuotaAccounts, 1); + + Mockito.doReturn(pair).when(quotaAccountDaoImplSpy).listAllQuotaAccount(null, null); + Mockito.doReturn(true).when(quotaAccountDaoImplSpy).getQuotaAccountEnabled(accountWithQuotaEnabled.getAccountId()); + Mockito.doReturn(false).when(quotaAccountDaoImplSpy).getQuotaAccountEnabled(accountWithQuotaDisabled.getAccountId()); + + int expected = quotaAccountDaoImplSpy.listAllQuotaAccount().size(); + Assert.assertEquals(1, expected); + } + +} diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaSummaryResponse.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaSummaryResponse.java index 863a0c3cd599..5b3526fba506 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaSummaryResponse.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaSummaryResponse.java @@ -69,6 +69,10 @@ public class QuotaSummaryResponse extends BaseResponse { @Param(description = "currency") private String currency; + @SerializedName("quotaenabled") + @Param(description = "if the account has the quota config enabled") + private boolean quotaEnabled; + public QuotaSummaryResponse() { super(); } @@ -152,4 +156,12 @@ public String getCurrency() { public void setCurrency(String currency) { this.currency = currency; } + + public boolean getQuotaEnabled() { + return quotaEnabled; + } + + public void setQuotaEnabled(boolean quotaEnabled) { + this.quotaEnabled = quotaEnabled; + } } 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 a8c28a53fe1f..4d6f34c73445 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 @@ -137,7 +137,7 @@ public String getConfigComponentName() { @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, QuotaAccountEnabled}; } @Override diff --git a/ui/src/config/section/plugin/quota.js b/ui/src/config/section/plugin/quota.js index aed256fcbb1c..d54bd2265c33 100644 --- a/ui/src/config/section/plugin/quota.js +++ b/ui/src/config/section/plugin/quota.js @@ -28,7 +28,10 @@ export default { title: 'label.quota.summary', icon: 'bars-outlined', permission: ['quotaSummary'], - columns: ['account', 'domain', 'state', 'currency', 'balance', 'quota'], + columns: [ + { + account: (record) => record.quotaenabled ? record.account : record.account + ' (quota-disabled)' + }, 'domain', 'state', 'currency', 'balance'], details: ['account', 'domain', 'state', 'currency', 'balance', 'quota', 'startdate', 'enddate'], component: shallowRef(() => import('@/views/plugins/quota/QuotaSummary.vue')), tabs: [ From d1f939e794ff2467f7d6aeabad816367940fa371 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 30 Aug 2022 09:33:28 -0300 Subject: [PATCH 2/9] Add check in quota usage calculation to skip accounts with quota disabled --- .../apache/cloudstack/quota/QuotaManagerImpl.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) 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 0b3840101277..bcc9abb2118c 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 @@ -29,6 +29,7 @@ import com.cloud.user.Account; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +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; @@ -133,9 +134,21 @@ public boolean stop() { return true; } + public boolean isQuotaEnabledForAccountDomain(final AccountVO account) { + boolean isQuotaAccountEnabled = QuotaConfig.QuotaAccountEnabled.valueIn(account.getAccountId()); + if (!isQuotaAccountEnabled) { + s_logger.debug(String.format("Considering usage records as calculated and skipping it because the account [%s] has the quota plugin disabled.", account)); + } + return isQuotaAccountEnabled; + } + + public boolean isUsageRecordsEmpty(final Pair, Integer> usageRecords) { + return usageRecords == null || usageRecords.first() == null || usageRecords.first().isEmpty(); + } + public List aggregatePendingQuotaRecordsForAccount(final AccountVO account, final Pair, Integer> usageRecords) { List quotaListForAccount = new ArrayList<>(); - if (usageRecords == null || usageRecords.first() == null || usageRecords.first().isEmpty()) { + if (isUsageRecordsEmpty(usageRecords) || isQuotaEnabledForAccountDomain(account)) { return quotaListForAccount; } s_logger.info("Getting pending quota records for account=" + account.getAccountName()); From 16c746b754ea3d895e9c78dc44c80e5d3c3c203c Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 30 Aug 2022 10:27:17 -0300 Subject: [PATCH 3/9] Set quota config enabled default to true --- .../java/org/apache/cloudstack/quota/constant/QuotaConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bc235eef5a90..40301c56e251 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,7 +57,7 @@ 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 conection.", true); - ConfigKey QuotaAccountEnabled = new ConfigKey<>("Advanced", Boolean.class, "quota.account.enabled", "false", "Indicates whether Quota plugin is enabled or not for " + + ConfigKey QuotaAccountEnabled = new ConfigKey<>("Advanced", Boolean.class, "quota.account.enabled", "true", "Indicates whether Quota plugin is enabled or not for " + "the account.", true, ConfigKey.Scope.Account); enum QuotaEmailTemplateTypes { From 9266ecb59c2eed101e6c90bfed5f7715b4537e26 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Tue, 30 Aug 2022 11:29:23 -0300 Subject: [PATCH 4/9] Fix if condition --- .../main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bcc9abb2118c..a2fb0137a860 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 @@ -148,7 +148,7 @@ public boolean isUsageRecordsEmpty(final Pair, Integer> public List aggregatePendingQuotaRecordsForAccount(final AccountVO account, final Pair, Integer> usageRecords) { List quotaListForAccount = new ArrayList<>(); - if (isUsageRecordsEmpty(usageRecords) || isQuotaEnabledForAccountDomain(account)) { + if (isUsageRecordsEmpty(usageRecords) || !isQuotaEnabledForAccountDomain(account)) { return quotaListForAccount; } s_logger.info("Getting pending quota records for account=" + account.getAccountName()); From 52cc004ab70d79ed48ebf8c8bf42f946e8ce2408 Mon Sep 17 00:00:00 2001 From: Bryan Lima <42067040+BryanMLima@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:17:22 -0300 Subject: [PATCH 5/9] Update condition to use primitive boolean expression Co-authored-by: dahn --- .../org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java index b90b03b0a4b8..084abcfc2b5d 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaAccountDaoImpl.java @@ -40,7 +40,7 @@ public class QuotaAccountDaoImpl extends GenericDaoBase im public List listAllQuotaAccount() { List accountsWithQuotaEnabled = new ArrayList<>(); for (QuotaAccountVO account : listAllQuotaAccount(null, null).first()) { - if (getQuotaAccountEnabled(account.getAccountId())) { + if (Boolean.TRUE.equals(getQuotaAccountEnabled(account.getAccountId()))) { accountsWithQuotaEnabled.add(account); continue; } From ede7d2f14593d22cec396fb1eba3ee1684c12f57 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Fri, 21 Oct 2022 13:51:07 -0300 Subject: [PATCH 6/9] Remove unused var --- .../main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java | 2 -- 1 file changed, 2 deletions(-) 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 cc8a94e30927..3826d6c82aa5 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 @@ -333,8 +333,6 @@ protected List createQuotaUsagesAccordingToQuotaTariffs(AccountVO pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null)); continue; } - - isQuotaEnabledForAccountDomain Pair, Boolean> pairQuotaTariffsPerUsageTypeAndHasActivationRule = mapQuotaTariffsPerUsageType.get(usageType); List quotaTariffs = pairQuotaTariffsPerUsageTypeAndHasActivationRule.first(); From d36038d7966f4c0beb89587ef2da301fc8c73126 Mon Sep 17 00:00:00 2001 From: Bryan Lima Date: Fri, 21 Oct 2022 14:15:13 -0300 Subject: [PATCH 7/9] Add quota state as a column in the Quota Summary view --- ui/public/locales/en.json | 2 ++ ui/public/locales/pt_BR.json | 2 ++ ui/src/components/view/ListView.vue | 3 +++ ui/src/config/section/plugin/quota.js | 11 ++++++++--- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index babd453baa1d..1f3d9f05f0fb 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -30,6 +30,7 @@ "label.account.name": "Account name", "label.account.specific": "Account-specific", "label.accounts": "Accounts", +"label.accountstate": "Account state", "label.accounttype": "Account type", "label.acl.export": "Export ACLs", "label.acl.id": "ACL ID", @@ -1361,6 +1362,7 @@ "label.quota.type.unit": "Usage unit", "label.quota.usage": "Quota consumption", "label.quota.value": "Quota value", +"label.quotastate": "Quota state", "label.quota_enforce": "Enforce Quota", "label.rados.monitor": "RADOS monitor", "label.rados.monitor.description": "The RADOS monitor(s). If there are multiple monitors, they are separated by comma. For example, \"192.168.0.1,192.168.0.2,192.168.0.3\", \"mon1, mon2, mon3\". IPv6 addresses must include square brackets, for example, \"[fc00:1234::1],[fc00:1234::2],[fc00:1234::3]\".", diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index d6b098d56ec0..ce665ffc0f47 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -29,6 +29,7 @@ "label.account.name": "Nome da conta", "label.account.specific": "Espec\u00edfico da conta", "label.accounts": "Contas", +"label.accountstate": "Estado da conta", "label.accounttype": "Tipo de conta", "label.acl.export": "Exportar ACLs", "label.acl.id": "ACL ID", @@ -1267,6 +1268,7 @@ "label.quota.statement.quota": "Utiliza\u00e7\u00e3o", "label.quota.statement.tariff": "Tarifa", "label.quota.summary": "Relat\u00f3rios", +"label.quotastate": "Estado da cota", "label.summary": "Sum\u00e1rio", "label.quota.tariff": "Tarifa", "label.quota.tariff.effectivedate": "Data efetiva", diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 690e4f0a8584..dc71216a54c2 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -195,6 +195,9 @@ +