Quota enable config to user/domain scope (#6690)

* Add quota plugin to accout/domain scope

* Add check in quota usage calculation to skip accounts with quota disabled

* Set quota config enabled default to true

* Fix if condition

* Update condition to use primitive boolean expression

Co-authored-by: dahn <daan.hoogland@gmail.com>

* Remove unused var

* Add quota state as a column in the Quota Summary view

* Remove trailling spaces

* Address review

Co-authored-by: dahn <daan.hoogland@gmail.com>
This commit is contained in:
Bryan Lima 2022-10-27 08:48:54 -03:00 committed by GitHub
parent adfaa730b1
commit f580a8d7a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 6 deletions

View File

@ -320,9 +320,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
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));
if (Boolean.FALSE.equals(shouldCalculateUsageRecord(account,usageRecord))) {
pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null));
continue;
}
@ -345,6 +343,21 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
return persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(pairsUsageAndQuotaUsage);
}
protected boolean shouldCalculateUsageRecord(AccountVO accountVO, UsageVO usageRecord) {
if (usageTypesToAvoidCalculation.contains(usageRecord.getUsageType())) {
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, usageTypesToAvoidCalculation));
return false;
}
if (Boolean.FALSE.equals(QuotaConfig.QuotaAccountEnabled.valueIn(accountVO.getAccountId()))) {
s_logger.debug(String.format("Considering usage record [%s] as calculated and skipping it because account [%s] has the quota plugin disabled.",
usageRecord, accountVO.reflectionToString()));
return false;
}
return true;
}
protected List<QuotaUsageVO> persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(List<Pair<UsageVO, QuotaUsageVO>> pairsUsageAndQuotaUsage) {
List<QuotaUsageVO> quotaUsages = new ArrayList<>();

View File

@ -60,6 +60,9 @@ public interface QuotaConfig {
public static final ConfigKey<Long> 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);
ConfigKey<Boolean> 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 {
QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT
}

View File

@ -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<QuotaAccountVO, Long> im
@Override
public List<QuotaAccountVO> listAllQuotaAccount() {
return listAllQuotaAccount(null, null).first();
List<QuotaAccountVO> accountsWithQuotaEnabled = new ArrayList<>();
for (QuotaAccountVO account : listAllQuotaAccount(null, null).first()) {
if (Boolean.TRUE.equals(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 class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> im
});
}
public Boolean getQuotaAccountEnabled(Long accountId) {
return QuotaConfig.QuotaAccountEnabled.valueIn(accountId);
}
}

View File

@ -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<QuotaAccountVO> allQuotaAccounts = List.of(accountWithQuotaDisabled);
Pair<List<QuotaAccountVO>,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<QuotaAccountVO> allQuotaAccounts = List.of(accountWithQuotaEnabled);
Pair<List<QuotaAccountVO>,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<QuotaAccountVO> allQuotaAccounts = List.of(accountWithQuotaEnabled, accountWithQuotaDisabled);
Pair<List<QuotaAccountVO>,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);
}
}

View File

@ -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 class QuotaSummaryResponse extends BaseResponse {
public void setCurrency(String currency) {
this.currency = currency;
}
public boolean getQuotaEnabled() {
return quotaEnabled;
}
public void setQuotaEnabled(boolean quotaEnabled) {
this.quotaEnabled = quotaEnabled;
}
}

View File

@ -141,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, QuotaActivationRuleTimeout};
QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender, QuotaSmtpEnabledSecurityProtocols, QuotaSmtpUseStartTLS, QuotaActivationRuleTimeout, QuotaAccountEnabled};
}
@Override

View File

@ -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]\".",

View File

@ -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",

View File

@ -195,6 +195,9 @@
<template #agentstate="{ text }">
<status :text="text ? text : ''" displayText />
</template>
<template #quotastate="{ text }">
<status :text="text ? text : ''" displayText />
</template>
<template #vlan="{ text, record }">
<a href="javascript:;">
<router-link v-if="$route.path === '/guestvlans'" :to="{ path: '/guestvlans/' + record.id }">{{ text }}</router-link>

View File

@ -28,7 +28,15 @@ export default {
title: 'label.quota.summary',
icon: 'bars-outlined',
permission: ['quotaSummary'],
columns: ['account', 'domain', 'state', 'currency', 'balance', 'quota'],
columns: ['account',
{
state: (record) => record.state.toLowerCase()
},
{
quotastate: (record) => record.quotaenabled ? 'Enabled' : 'Disabled'
}, 'domain', 'currency', 'balance'
],
columnNames: ['account', 'accountstate', 'quotastate', 'domain', 'currency', 'currentbalance'],
details: ['account', 'domain', 'state', 'currency', 'balance', 'quota', 'startdate', 'enddate'],
component: shallowRef(() => import('@/views/plugins/quota/QuotaSummary.vue')),
tabs: [