Quota tariff order (#8347)

This commit is contained in:
João Jandre 2024-07-14 11:05:15 -03:00 committed by GitHub
parent a87778be9a
commit b9c7275c25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 351 additions and 21 deletions

View File

@ -150,3 +150,7 @@ SET
WHERE WHERE
name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth") name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth")
AND value NOT IN ("true", "y", "t", "1", "on", "yes"); AND value NOT IN ("true", "y", "t", "1", "on", "yes");
-- Quota inject tariff result into subsequent ones
CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.quota_tariff', 'position', 'bigint(20) NOT NULL DEFAULT 1 COMMENT "Position in the execution sequence for tariffs of the same type"');

View File

@ -20,6 +20,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -36,6 +37,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable; import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper; import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables; import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
import org.apache.cloudstack.quota.constant.QuotaConfig; import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes; import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao; import org.apache.cloudstack.quota.dao.QuotaAccountDao;
@ -371,9 +373,22 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord); PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord);
BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO; BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO;
quotaTariffs.sort(Comparator.comparing(QuotaTariffVO::getPosition));
List<Tariff> lastTariffs = new ArrayList<>();
for (QuotaTariffVO quotaTariff : quotaTariffs) { for (QuotaTariffVO quotaTariff : quotaTariffs) {
if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) { if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) {
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables));
BigDecimal tariffValue = getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables, lastTariffs);
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(tariffValue);
Tariff tariffPresetVariable = new Tariff();
tariffPresetVariable.setId(quotaTariff.getUuid());
tariffPresetVariable.setValue(tariffValue);
lastTariffs.add(tariffPresetVariable);
} }
} }
@ -401,7 +416,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
* <li>If the activation rule result in something else, returns {@link BigDecimal#ZERO}.</li> * <li>If the activation rule result in something else, returns {@link BigDecimal#ZERO}.</li>
* </ul> * </ul>
*/ */
protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables) { protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables, List<Tariff> lastAppliedTariffsList) {
String activationRule = quotaTariff.getActivationRule(); String activationRule = quotaTariff.getActivationRule();
BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue(); BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue();
String quotaTariffToString = quotaTariff.toString(usageAggregationTimeZone); String quotaTariffToString = quotaTariff.toString(usageAggregationTimeZone);
@ -413,6 +428,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
} }
injectPresetVariablesIntoJsInterpreter(jsInterpreter, presetVariables); injectPresetVariablesIntoJsInterpreter(jsInterpreter, presetVariables);
jsInterpreter.injectVariable("lastTariffs", lastAppliedTariffsList.toString());
String scriptResult = jsInterpreter.executeScript(activationRule).toString(); String scriptResult = jsInterpreter.executeScript(activationRule).toString();

View File

@ -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;
import java.math.BigDecimal;
public class Tariff extends GenericPresetVariable {
private BigDecimal value;
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
fieldNamesToIncludeInToString.add("value");
}
}

View File

@ -93,6 +93,10 @@ public class QuotaTariffVO implements QuotaTariff {
@Temporal(value = TemporalType.TIMESTAMP) @Temporal(value = TemporalType.TIMESTAMP)
private Date endDate; private Date endDate;
@Column(name = "position")
protected Integer position;
public QuotaTariffVO() { public QuotaTariffVO() {
} }
@ -120,6 +124,7 @@ public class QuotaTariffVO implements QuotaTariff {
this.setDescription(that.getDescription()); this.setDescription(that.getDescription());
this.setActivationRule(that.getActivationRule()); this.setActivationRule(that.getActivationRule());
this.setEndDate(that.getEndDate()); this.setEndDate(that.getEndDate());
this.setPosition(that.getPosition());
} }
public void setId(Long id) { public void setId(Long id) {
@ -263,6 +268,15 @@ public class QuotaTariffVO implements QuotaTariff {
return true; return true;
} }
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
@Override @Override
public String toString() { public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "usageName"); return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "usageName");

View File

@ -29,6 +29,7 @@ import org.apache.cloudstack.quota.activationrule.presetvariables.Domain;
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable; import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper; import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables; import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
import org.apache.cloudstack.quota.activationrule.presetvariables.Value; import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
import org.apache.cloudstack.quota.constant.QuotaTypes; import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaTariffDao; import org.apache.cloudstack.quota.dao.QuotaTariffDao;
@ -395,7 +396,7 @@ public class QuotaManagerImplTest {
Mockito.doReturn(null).when(quotaTariffVoMock).getActivationRule(); Mockito.doReturn(null).when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.ONE).when(quotaTariffVoMock).getCurrencyValue(); Mockito.doReturn(BigDecimal.ONE).when(quotaTariffVoMock).getCurrencyValue();
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null); BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null, null);
Assert.assertEquals(BigDecimal.ONE, result); Assert.assertEquals(BigDecimal.ONE, result);
} }
@ -405,7 +406,7 @@ public class QuotaManagerImplTest {
Mockito.doReturn("").when(quotaTariffVoMock).getActivationRule(); Mockito.doReturn("").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue(); Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null); BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null, null);
Assert.assertEquals(BigDecimal.TEN, result); Assert.assertEquals(BigDecimal.TEN, result);
} }
@ -413,13 +414,15 @@ public class QuotaManagerImplTest {
@Test @Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsNumberReturnIt() { public void getQuotaTariffValueToBeAppliedTestScriptResultIsNumberReturnIt() {
BigDecimal expected = new BigDecimal(50.1); BigDecimal expected = new BigDecimal(50.1);
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule(); Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue(); Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any()); Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn(expected).when(jsInterpreterMock).executeScript(Mockito.anyString()); Mockito.doReturn(expected).when(jsInterpreterMock).executeScript(Mockito.anyString());
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock); BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
Assert.assertEquals(expected, result); Assert.assertEquals(expected, result);
} }
@ -427,37 +430,42 @@ public class QuotaManagerImplTest {
@Test @Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsTrueReturnTariffValue() { public void getQuotaTariffValueToBeAppliedTestScriptResultIsTrueReturnTariffValue() {
BigDecimal expected = new BigDecimal(236.84); BigDecimal expected = new BigDecimal(236.84);
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule(); Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(expected).when(quotaTariffVoMock).getCurrencyValue(); Mockito.doReturn(expected).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any()); Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn(true).when(jsInterpreterMock).executeScript(Mockito.anyString()); Mockito.doReturn(true).when(jsInterpreterMock).executeScript(Mockito.anyString());
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock); BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
Assert.assertEquals(expected, result); Assert.assertEquals(expected, result);
} }
@Test @Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsFalseReturnZero() { public void getQuotaTariffValueToBeAppliedTestScriptResultIsFalseReturnZero() {
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule(); Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue(); Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any()); Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn(false).when(jsInterpreterMock).executeScript(Mockito.anyString()); Mockito.doReturn(false).when(jsInterpreterMock).executeScript(Mockito.anyString());
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock); BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
Assert.assertEquals(BigDecimal.ZERO, result); Assert.assertEquals(BigDecimal.ZERO, result);
} }
@Test @Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsNotBooleanNorNumericReturnZero() { public void getQuotaTariffValueToBeAppliedTestScriptResultIsNotBooleanNorNumericReturnZero() {
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule(); Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue(); Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any()); Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn("test").when(jsInterpreterMock).executeScript(Mockito.anyString()); Mockito.doReturn("test").when(jsInterpreterMock).executeScript(Mockito.anyString());
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock); BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
Assert.assertEquals(BigDecimal.ZERO, result); Assert.assertEquals(BigDecimal.ZERO, result);
} }
@ -477,10 +485,7 @@ public class QuotaManagerImplTest {
@Test @Test
public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnZero() { public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnZero() {
List<QuotaTariffVO> tariffs = new ArrayList<>(); List<QuotaTariffVO> tariffs = createTariffList();
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
Mockito.doReturn(false).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString()); Mockito.doReturn(false).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, ""); BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
@ -497,13 +502,10 @@ public class QuotaManagerImplTest {
@Test @Test
public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnAggregation() { public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnAggregation() {
List<QuotaTariffVO> tariffs = new ArrayList<>(); List<QuotaTariffVO> tariffs = createTariffList();
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(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()); Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, ""); BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
Assert.assertEquals(BigDecimal.TEN.multiply(new BigDecimal(2)), result); Assert.assertEquals(BigDecimal.TEN.multiply(new BigDecimal(2)), result);
@ -528,4 +530,25 @@ public class QuotaManagerImplTest {
Assert.assertEquals(quotaUsageVoMock1, result.get(0)); Assert.assertEquals(quotaUsageVoMock1, result.get(0));
Assert.assertEquals(quotaUsageVoMock2, result.get(1)); Assert.assertEquals(quotaUsageVoMock2, result.get(1));
} }
private static List<QuotaTariffVO> createTariffList() {
List<QuotaTariffVO> tariffs = new ArrayList<>();
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.forEach(quotaTariffVO -> quotaTariffVO.setPosition(1));
return tariffs;
}
private static List<Tariff> createLastAppliedTariffsPresetVariableList(int numberOfTariffs) {
List<Tariff> lastTariffs = new ArrayList<>();
for (int i = 0; i < numberOfTariffs; i++) {
Tariff tariff = new Tariff();
tariff.setId(String.valueOf(i));
tariff.setValue(BigDecimal.valueOf(i));
lastTariffs.add(tariff);
}
return lastTariffs;
}
} }

View File

@ -68,6 +68,9 @@ public class QuotaTariffCreateCmd extends BaseCmd {
ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS) ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS)
private Date endDate; private Date endDate;
@Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0")
private Integer position;
@Override @Override
public void execute() { public void execute() {
CallContext.current().setEventDetails(String.format("Tariff: %s, description: %s, value: %s", getName(), getDescription(), getValue())); CallContext.current().setEventDetails(String.format("Tariff: %s, description: %s, value: %s", getName(), getDescription(), getValue()));
@ -139,4 +142,13 @@ public class QuotaTariffCreateCmd extends BaseCmd {
public ApiCommandResourceType getApiResourceType() { public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.QuotaTariff; return ApiCommandResourceType.QuotaTariff;
} }
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
} }

View File

@ -69,6 +69,9 @@ public class QuotaTariffUpdateCmd extends BaseCmd {
"value will be applied. Inform empty to remove the activation rule.", length = 65535, since = "4.18.0.0") "value will be applied. Inform empty to remove the activation rule.", length = 65535, since = "4.18.0.0")
private String activationRule; private String activationRule;
@Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0")
private Integer position;
public Integer getUsageType() { public Integer getUsageType() {
return usageType; return usageType;
} }
@ -130,4 +133,13 @@ public class QuotaTariffUpdateCmd extends BaseCmd {
public ApiCommandResourceType getApiResourceType() { public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.QuotaTariff; return ApiCommandResourceType.QuotaTariff;
} }
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
} }

View File

@ -80,6 +80,7 @@ import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -151,6 +152,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
response.setDescription(tariff.getDescription()); response.setDescription(tariff.getDescription());
response.setId(tariff.getUuid()); response.setId(tariff.getUuid());
response.setRemoved(tariff.getRemoved()); response.setRemoved(tariff.getRemoved());
response.setPosition(tariff.getPosition());
return response; return response;
} }
@ -414,6 +416,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
String description = cmd.getDescription(); String description = cmd.getDescription();
String activationRule = cmd.getActivationRule(); String activationRule = cmd.getActivationRule();
Date now = new Date(); Date now = new Date();
Integer position = cmd.getPosition();
warnQuotaTariffUpdateDeprecatedFields(cmd); warnQuotaTariffUpdateDeprecatedFields(cmd);
@ -428,7 +431,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
currentQuotaTariff.setRemoved(now); currentQuotaTariff.setRemoved(now);
QuotaTariffVO newQuotaTariff = persistNewQuotaTariff(currentQuotaTariff, name, 0, currentQuotaTariffStartDate, cmd.getEntityOwnerId(), endDate, value, description, QuotaTariffVO newQuotaTariff = persistNewQuotaTariff(currentQuotaTariff, name, 0, currentQuotaTariffStartDate, cmd.getEntityOwnerId(), endDate, value, description,
activationRule); activationRule, position);
_quotaTariffDao.updateQuotaTariff(currentQuotaTariff); _quotaTariffDao.updateQuotaTariff(currentQuotaTariff);
CallContext.current().setEventResourceId(newQuotaTariff.getId()); CallContext.current().setEventResourceId(newQuotaTariff.getId());
@ -449,7 +452,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
} }
protected QuotaTariffVO persistNewQuotaTariff(QuotaTariffVO currentQuotaTariff, String name, int usageType, Date startDate, Long entityOwnerId, Date endDate, Double value, protected QuotaTariffVO persistNewQuotaTariff(QuotaTariffVO currentQuotaTariff, String name, int usageType, Date startDate, Long entityOwnerId, Date endDate, Double value,
String description, String activationRule) { String description, String activationRule, Integer position) {
QuotaTariffVO newQuotaTariff = getNewQuotaTariffObject(currentQuotaTariff, name, usageType); QuotaTariffVO newQuotaTariff = getNewQuotaTariffObject(currentQuotaTariff, name, usageType);
@ -461,6 +464,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
validateValueOnCreatingNewQuotaTariff(newQuotaTariff, value); validateValueOnCreatingNewQuotaTariff(newQuotaTariff, value);
validateStringsOnCreatingNewQuotaTariff(newQuotaTariff::setDescription, description); validateStringsOnCreatingNewQuotaTariff(newQuotaTariff::setDescription, description);
validateStringsOnCreatingNewQuotaTariff(newQuotaTariff::setActivationRule, activationRule); validateStringsOnCreatingNewQuotaTariff(newQuotaTariff::setActivationRule, activationRule);
validatePositionOnCreatingNewQuotaTariff(newQuotaTariff, position);
_quotaTariffDao.addQuotaTariff(newQuotaTariff); _quotaTariffDao.addQuotaTariff(newQuotaTariff);
return newQuotaTariff; return newQuotaTariff;
@ -481,6 +485,13 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
return newQuotaTariff; return newQuotaTariff;
} }
protected void validatePositionOnCreatingNewQuotaTariff(QuotaTariffVO newQuotaTariff, Integer position) {
if (position != null) {
newQuotaTariff.setPosition(position);
}
}
protected void validateStringsOnCreatingNewQuotaTariff(Consumer<String> method, String value){ protected void validateStringsOnCreatingNewQuotaTariff(Consumer<String> method, String value){
if (value != null) { if (value != null) {
method.accept(value.isBlank() ? null : value); method.accept(value.isBlank() ? null : value);
@ -663,6 +674,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
Double value = cmd.getValue(); Double value = cmd.getValue();
String description = cmd.getDescription(); String description = cmd.getDescription();
String activationRule = cmd.getActivationRule(); String activationRule = cmd.getActivationRule();
Integer position = ObjectUtils.defaultIfNull(cmd.getPosition(), 1);
QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name); QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name);
@ -675,7 +687,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
"Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate)); "Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate));
} }
QuotaTariffVO newQuotaTariff = persistNewQuotaTariff(null, name, usageType, startDate, cmd.getEntityOwnerId(), endDate, value, description, activationRule); QuotaTariffVO newQuotaTariff = persistNewQuotaTariff(null, name, usageType, startDate, cmd.getEntityOwnerId(), endDate, value, description, activationRule, position);
CallContext.current().setEventResourceId(newQuotaTariff.getId()); CallContext.current().setEventResourceId(newQuotaTariff.getId());

View File

@ -83,6 +83,11 @@ public class QuotaTariffResponse extends BaseResponse {
@Param(description = "when the quota tariff was removed") @Param(description = "when the quota tariff was removed")
private Date removed; private Date removed;
@SerializedName("position")
@Param(description = "position in the execution sequence for tariffs of the same type")
private Integer position;
public QuotaTariffResponse() { public QuotaTariffResponse() {
super(); super();
this.setObjectName("quotatariff"); this.setObjectName("quotatariff");
@ -172,4 +177,12 @@ public class QuotaTariffResponse extends BaseResponse {
this.removed = removed; this.removed = removed;
} }
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
} }

View File

@ -372,8 +372,10 @@ public class QuotaResponseBuilderImplTest extends TestCase {
Mockito.doNothing().when(quotaResponseBuilderSpy).validateValueOnCreatingNewQuotaTariff(Mockito.any(QuotaTariffVO.class), Mockito.anyDouble()); Mockito.doNothing().when(quotaResponseBuilderSpy).validateValueOnCreatingNewQuotaTariff(Mockito.any(QuotaTariffVO.class), Mockito.anyDouble());
Mockito.doNothing().when(quotaResponseBuilderSpy).validateStringsOnCreatingNewQuotaTariff(Mockito.any(Consumer.class), Mockito.anyString()); Mockito.doNothing().when(quotaResponseBuilderSpy).validateStringsOnCreatingNewQuotaTariff(Mockito.any(Consumer.class), Mockito.anyString());
Mockito.doReturn(quotaTariffVoMock).when(quotaTariffDaoMock).addQuotaTariff(Mockito.any(QuotaTariffVO.class)); Mockito.doReturn(quotaTariffVoMock).when(quotaTariffDaoMock).addQuotaTariff(Mockito.any(QuotaTariffVO.class));
Mockito.doNothing().when(quotaResponseBuilderSpy).validatePositionOnCreatingNewQuotaTariff(Mockito.any(QuotaTariffVO.class), Mockito.anyInt());
quotaResponseBuilderSpy.persistNewQuotaTariff(quotaTariffVoMock, "", 1, date, 1l, date, 1.0, "", "");
quotaResponseBuilderSpy.persistNewQuotaTariff(quotaTariffVoMock, "", 1, date, 1l, date, 1.0, "", "", 2);
Mockito.verify(quotaTariffDaoMock).addQuotaTariff(Mockito.any(QuotaTariffVO.class)); Mockito.verify(quotaTariffDaoMock).addQuotaTariff(Mockito.any(QuotaTariffVO.class));
} }
@ -553,4 +555,18 @@ public class QuotaResponseBuilderImplTest extends TestCase {
assertEquals(2, result.getEmailTemplateId()); assertEquals(2, result.getEmailTemplateId());
assertFalse(result.isEnabled()); assertFalse(result.isEnabled());
} }
@Test
public void validatePositionOnCreatingNewQuotaTariffTestNullValueDoNothing() {
quotaResponseBuilderSpy.validatePositionOnCreatingNewQuotaTariff(quotaTariffVoMock, null);
Mockito.verify(quotaTariffVoMock, Mockito.never()).setPosition(Mockito.any());
}
@Test
public void validatePositionOnCreatingNewQuotaTariffTestAnyValueIsSet() {
Integer position = 1;
quotaResponseBuilderSpy.validatePositionOnCreatingNewQuotaTariff(quotaTariffVoMock, position);
Mockito.verify(quotaTariffVoMock).setPosition(position);
}
} }

View File

@ -0,0 +1,175 @@
# 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.
""" Test cases for checking quota API
"""
# Import Local Modules
import tools.marvin.marvin
from tools.marvin.marvin.cloudstackTestCase import *
from tools.marvin.marvin.cloudstackAPI import *
from tools.marvin.marvin.lib.utils import *
from tools.marvin.marvin.lib.base import *
from tools.marvin.marvin.lib.common import *
from nose.plugins.attrib import attr
# Import System modules
import time
class TestQuotaTariffOrder(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestQuotaTariffOrder, cls).getClsTestClient()
cls.api_client = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.api_client)
cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
cls._cleanup = []
# Create Account
cls.account = Account.create(
cls.api_client,
cls.services["account"],
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
cls.services["account"] = cls.account.name
return
@classmethod
def tearDownClass(cls):
super(TestQuotaTariffOrder, cls).tearDownClass()
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
self.tariffs = []
return
def tearDown(self):
self.delete_tariffs()
super(TestQuotaTariffOrder, self).tearDown()
def delete_tariffs(self):
for tariff in self.tariffs:
cmd = quotaTariffDelete.quotaTariffDeleteCmd()
cmd.id = tariff.uuid
self.api_client.quotaTariffDelete(cmd)
@attr(
tags=[
"advanced",
"smoke"],
required_hardware="false")
def test_01_quota_tariff_order(self):
"""Test Quota Tariff Order
"""
cmd = quotaTariffCreate.quotaTariffCreateCmd()
cmd.name = 'tf1'
cmd.value = '1'
cmd.activationrule = '10'
cmd.usagetype = '22'
cmd.position = '2'
self.tariffs.append(self.api_client.quotaTariffCreate(cmd))
cmd = quotaTariffCreate.quotaTariffCreateCmd()
cmd.name = 'tf2'
cmd.value = '1'
cmd.activationrule = 'lastTariffs[lastTariffs.length -1].value + 7'
cmd.usagetype = '22'
cmd.position = '3'
self.tariffs.append(self.api_client.quotaTariffCreate(cmd))
cmd = quotaTariffCreate.quotaTariffCreateCmd()
cmd.name = 'tf3'
cmd.value = '1'
cmd.activationrule = 'lastTariffs[lastTariffs.length -2].value + lastTariffs[lastTariffs.length -1].value'
cmd.usagetype = '22'
cmd.position = '4'
self.tariffs.append(self.api_client.quotaTariffCreate(cmd))
cmd = quotaCredits.quotaCreditsCmd()
cmd.account = self.account.name
cmd.domainid = self.domain.id
cmd.value = 54
self.api_client.quotaCredits(cmd)
# Fetch account ID from account_uuid
self.debug("select id from account where uuid = '%s';"
% self.account.id)
qresultset = self.dbclient.execute(
"select id from account where uuid = '%s';"
% self.account.id
)
account_id = qresultset[0][0]
self.debug("SELECT id from `domain` d WHERE uuid = '%s';"
% self.domain.id)
qresultset = self.dbclient.execute(
"SELECT id from `domain` d WHERE uuid = '%s';"
% self.domain.id
)
domain_id = qresultset[0][0]
self.debug("SELECT id from data_center dc where dc.uuid = '%s';"
% self.zone.id)
qresultset = self.dbclient.execute(
"SELECT id from data_center dc where dc.uuid = '%s';"
% self.zone.id
)
zone_id = qresultset[0][0]
start = datetime.datetime.now() + datetime.timedelta(seconds=1)
end = datetime.datetime.now() + datetime.timedelta(hours=1)
query = "INSERT INTO cloud_usage.cloud_usage (zone_id,account_id,domain_id,description,usage_display,"
"usage_type,raw_usage,vm_instance_id,vm_name,offering_id,template_id,usage_id,`type`,`size`,"
"network_id,start_date,end_date,virtual_size,cpu_speed,cpu_cores,memory,quota_calculated,"
"is_hidden,state) VALUES ('{}','{}','{}','Test','1 Hrs',22,1,NULL,NULL,NULL,NULL,NULL,"
"'VirtualMachine',NULL,NULL,'{}','{}',NULL,NULL,NULL,NULL,0,0,NULL);".format(zone_id, account_id, domain_id, start, end)
self.debug(query)
self.dbclient.execute(
query)
cmd = quotaUpdate.quotaUpdateCmd()
self.api_client.quotaUpdate(cmd)
cmd = quotaBalance.quotaBalanceCmd()
cmd.domainid = self.account.domainid
cmd.account = self.account.name
response = self.apiclient.quotaBalance(cmd)
self.debug(f"Quota Balance: {response.balance}")
self.assertEqual(response.balance.startquota, 0, f"startQuota is supposed to be 0 but was {response.balance.startquota}")
return