mirror of https://github.com/apache/cloudstack.git
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:
parent
adfaa730b1
commit
f580a8d7a2
|
|
@ -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<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]\".",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
Loading…
Reference in New Issue