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) {
|
for (UsageVO usageRecord : usageRecords) {
|
||||||
int usageType = usageRecord.getUsageType();
|
int usageType = usageRecord.getUsageType();
|
||||||
|
|
||||||
if (usageTypesToAvoidCalculation.contains(usageType)) {
|
if (Boolean.FALSE.equals(shouldCalculateUsageRecord(account,usageRecord))) {
|
||||||
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));
|
pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -345,6 +343,21 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
||||||
return persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(pairsUsageAndQuotaUsage);
|
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) {
|
protected List<QuotaUsageVO> persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(List<Pair<UsageVO, QuotaUsageVO>> pairsUsageAndQuotaUsage) {
|
||||||
List<QuotaUsageVO> quotaUsages = new ArrayList<>();
|
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,"
|
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);
|
+ " 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 {
|
enum QuotaEmailTemplateTypes {
|
||||||
QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT
|
QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@
|
||||||
//under the License.
|
//under the License.
|
||||||
package org.apache.cloudstack.quota.dao;
|
package org.apache.cloudstack.quota.dao;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.quota.constant.QuotaConfig;
|
||||||
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
|
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
@ -36,7 +38,15 @@ public class QuotaAccountDaoImpl extends GenericDaoBase<QuotaAccountVO, Long> im
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<QuotaAccountVO> listAllQuotaAccount() {
|
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
|
@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")
|
@Param(description = "currency")
|
||||||
private String currency;
|
private String currency;
|
||||||
|
|
||||||
|
@SerializedName("quotaenabled")
|
||||||
|
@Param(description = "if the account has the quota config enabled")
|
||||||
|
private boolean quotaEnabled;
|
||||||
|
|
||||||
public QuotaSummaryResponse() {
|
public QuotaSummaryResponse() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
@ -152,4 +156,12 @@ public class QuotaSummaryResponse extends BaseResponse {
|
||||||
public void setCurrency(String currency) {
|
public void setCurrency(String currency) {
|
||||||
this.currency = 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
|
@Override
|
||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[] {QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaStatementPeriod, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout,
|
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
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
"label.account.name": "Account name",
|
"label.account.name": "Account name",
|
||||||
"label.account.specific": "Account-specific",
|
"label.account.specific": "Account-specific",
|
||||||
"label.accounts": "Accounts",
|
"label.accounts": "Accounts",
|
||||||
|
"label.accountstate": "Account state",
|
||||||
"label.accounttype": "Account type",
|
"label.accounttype": "Account type",
|
||||||
"label.acl.export": "Export ACLs",
|
"label.acl.export": "Export ACLs",
|
||||||
"label.acl.id": "ACL ID",
|
"label.acl.id": "ACL ID",
|
||||||
|
|
@ -1361,6 +1362,7 @@
|
||||||
"label.quota.type.unit": "Usage unit",
|
"label.quota.type.unit": "Usage unit",
|
||||||
"label.quota.usage": "Quota consumption",
|
"label.quota.usage": "Quota consumption",
|
||||||
"label.quota.value": "Quota value",
|
"label.quota.value": "Quota value",
|
||||||
|
"label.quotastate": "Quota state",
|
||||||
"label.quota_enforce": "Enforce Quota",
|
"label.quota_enforce": "Enforce Quota",
|
||||||
"label.rados.monitor": "RADOS monitor",
|
"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]\".",
|
"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.name": "Nome da conta",
|
||||||
"label.account.specific": "Espec\u00edfico da conta",
|
"label.account.specific": "Espec\u00edfico da conta",
|
||||||
"label.accounts": "Contas",
|
"label.accounts": "Contas",
|
||||||
|
"label.accountstate": "Estado da conta",
|
||||||
"label.accounttype": "Tipo de conta",
|
"label.accounttype": "Tipo de conta",
|
||||||
"label.acl.export": "Exportar ACLs",
|
"label.acl.export": "Exportar ACLs",
|
||||||
"label.acl.id": "ACL ID",
|
"label.acl.id": "ACL ID",
|
||||||
|
|
@ -1267,6 +1268,7 @@
|
||||||
"label.quota.statement.quota": "Utiliza\u00e7\u00e3o",
|
"label.quota.statement.quota": "Utiliza\u00e7\u00e3o",
|
||||||
"label.quota.statement.tariff": "Tarifa",
|
"label.quota.statement.tariff": "Tarifa",
|
||||||
"label.quota.summary": "Relat\u00f3rios",
|
"label.quota.summary": "Relat\u00f3rios",
|
||||||
|
"label.quotastate": "Estado da cota",
|
||||||
"label.summary": "Sum\u00e1rio",
|
"label.summary": "Sum\u00e1rio",
|
||||||
"label.quota.tariff": "Tarifa",
|
"label.quota.tariff": "Tarifa",
|
||||||
"label.quota.tariff.effectivedate": "Data efetiva",
|
"label.quota.tariff.effectivedate": "Data efetiva",
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,9 @@
|
||||||
<template #agentstate="{ text }">
|
<template #agentstate="{ text }">
|
||||||
<status :text="text ? text : ''" displayText />
|
<status :text="text ? text : ''" displayText />
|
||||||
</template>
|
</template>
|
||||||
|
<template #quotastate="{ text }">
|
||||||
|
<status :text="text ? text : ''" displayText />
|
||||||
|
</template>
|
||||||
<template #vlan="{ text, record }">
|
<template #vlan="{ text, record }">
|
||||||
<a href="javascript:;">
|
<a href="javascript:;">
|
||||||
<router-link v-if="$route.path === '/guestvlans'" :to="{ path: '/guestvlans/' + record.id }">{{ text }}</router-link>
|
<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',
|
title: 'label.quota.summary',
|
||||||
icon: 'bars-outlined',
|
icon: 'bars-outlined',
|
||||||
permission: ['quotaSummary'],
|
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'],
|
details: ['account', 'domain', 'state', 'currency', 'balance', 'quota', 'startdate', 'enddate'],
|
||||||
component: shallowRef(() => import('@/views/plugins/quota/QuotaSummary.vue')),
|
component: shallowRef(() => import('@/views/plugins/quota/QuotaSummary.vue')),
|
||||||
tabs: [
|
tabs: [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue