Skip to content

Commit 05c6eba

Browse files
JoaoJandredhslove
authored andcommitted
Quota tariff order (apache#8347)
1 parent 9d1d752 commit 05c6eba

File tree

11 files changed

+351
-21
lines changed

11 files changed

+351
-21
lines changed

engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,7 @@ SET
150150
WHERE
151151
name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth")
152152
AND value NOT IN ("true", "y", "t", "1", "on", "yes");
153+
154+
155+
-- Quota inject tariff result into subsequent ones
156+
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"');

framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.math.RoundingMode;
2121
import java.util.ArrayList;
2222
import java.util.Arrays;
23+
import java.util.Comparator;
2324
import java.util.Date;
2425
import java.util.HashMap;
2526
import java.util.LinkedHashSet;
@@ -36,6 +37,7 @@
3637
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
3738
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
3839
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
40+
import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
3941
import org.apache.cloudstack.quota.constant.QuotaConfig;
4042
import org.apache.cloudstack.quota.constant.QuotaTypes;
4143
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
@@ -371,9 +373,22 @@ protected BigDecimal aggregateQuotaTariffsValues(UsageVO usageRecord, List<Quota
371373
PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord);
372374
BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO;
373375

376+
quotaTariffs.sort(Comparator.comparing(QuotaTariffVO::getPosition));
377+
378+
List<Tariff> lastTariffs = new ArrayList<>();
379+
380+
374381
for (QuotaTariffVO quotaTariff : quotaTariffs) {
375382
if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) {
376-
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables));
383+
384+
BigDecimal tariffValue = getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables, lastTariffs);
385+
386+
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(tariffValue);
387+
388+
Tariff tariffPresetVariable = new Tariff();
389+
tariffPresetVariable.setId(quotaTariff.getUuid());
390+
tariffPresetVariable.setValue(tariffValue);
391+
lastTariffs.add(tariffPresetVariable);
377392
}
378393
}
379394

@@ -401,7 +416,7 @@ protected PresetVariables getPresetVariables(boolean hasAnyQuotaTariffWithActiva
401416
* <li>If the activation rule result in something else, returns {@link BigDecimal#ZERO}.</li>
402417
* </ul>
403418
*/
404-
protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables) {
419+
protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables, List<Tariff> lastAppliedTariffsList) {
405420
String activationRule = quotaTariff.getActivationRule();
406421
BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue();
407422
String quotaTariffToString = quotaTariff.toString(usageAggregationTimeZone);
@@ -413,6 +428,7 @@ protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, J
413428
}
414429

415430
injectPresetVariablesIntoJsInterpreter(jsInterpreter, presetVariables);
431+
jsInterpreter.injectVariable("lastTariffs", lastAppliedTariffsList.toString());
416432

417433
String scriptResult = jsInterpreter.executeScript(activationRule).toString();
418434

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.quota.activationrule.presetvariables;
19+
20+
import java.math.BigDecimal;
21+
22+
public class Tariff extends GenericPresetVariable {
23+
private BigDecimal value;
24+
25+
public BigDecimal getValue() {
26+
return value;
27+
}
28+
29+
public void setValue(BigDecimal value) {
30+
this.value = value;
31+
fieldNamesToIncludeInToString.add("value");
32+
}
33+
}

framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ public class QuotaTariffVO implements QuotaTariff {
9393
@Temporal(value = TemporalType.TIMESTAMP)
9494
private Date endDate;
9595

96+
@Column(name = "position")
97+
protected Integer position;
98+
99+
96100
public QuotaTariffVO() {
97101
}
98102

@@ -120,6 +124,7 @@ public QuotaTariffVO(QuotaTariffVO that) {
120124
this.setDescription(that.getDescription());
121125
this.setActivationRule(that.getActivationRule());
122126
this.setEndDate(that.getEndDate());
127+
this.setPosition(that.getPosition());
123128
}
124129

125130
public void setId(Long id) {
@@ -263,6 +268,15 @@ public boolean setUsageTypeData(int usageType) {
263268
return true;
264269
}
265270

271+
public Integer getPosition() {
272+
return position;
273+
}
274+
275+
public void setPosition(Integer position) {
276+
this.position = position;
277+
}
278+
279+
266280
@Override
267281
public String toString() {
268282
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "usageName");

framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaManagerImplTest.java

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
3030
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
3131
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
32+
import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
3233
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
3334
import org.apache.cloudstack.quota.constant.QuotaTypes;
3435
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
@@ -395,7 +396,7 @@ public void getQuotaTariffValueToBeAppliedTestActivationRuleIsNullReturnTariffVa
395396
Mockito.doReturn(null).when(quotaTariffVoMock).getActivationRule();
396397
Mockito.doReturn(BigDecimal.ONE).when(quotaTariffVoMock).getCurrencyValue();
397398

398-
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null);
399+
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null, null);
399400

400401
Assert.assertEquals(BigDecimal.ONE, result);
401402
}
@@ -405,59 +406,66 @@ public void getQuotaTariffValueToBeAppliedTestActivationRuleIsEmptyReturnTariffV
405406
Mockito.doReturn("").when(quotaTariffVoMock).getActivationRule();
406407
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
407408

408-
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null);
409+
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null, null);
409410

410411
Assert.assertEquals(BigDecimal.TEN, result);
411412
}
412413

413414
@Test
414415
public void getQuotaTariffValueToBeAppliedTestScriptResultIsNumberReturnIt() {
415416
BigDecimal expected = new BigDecimal(50.1);
417+
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
418+
416419

417420
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
418421
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
419422
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
420423
Mockito.doReturn(expected).when(jsInterpreterMock).executeScript(Mockito.anyString());
421424

422-
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
425+
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
423426

424427
Assert.assertEquals(expected, result);
425428
}
426429

427430
@Test
428431
public void getQuotaTariffValueToBeAppliedTestScriptResultIsTrueReturnTariffValue() {
429432
BigDecimal expected = new BigDecimal(236.84);
433+
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
430434

431435
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
432436
Mockito.doReturn(expected).when(quotaTariffVoMock).getCurrencyValue();
433437
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
434438
Mockito.doReturn(true).when(jsInterpreterMock).executeScript(Mockito.anyString());
435439

436-
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
440+
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
437441

438442
Assert.assertEquals(expected, result);
439443
}
440444

441445
@Test
442446
public void getQuotaTariffValueToBeAppliedTestScriptResultIsFalseReturnZero() {
447+
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
448+
443449
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
444450
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
445451
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
446452
Mockito.doReturn(false).when(jsInterpreterMock).executeScript(Mockito.anyString());
447453

448-
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
454+
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
449455

450456
Assert.assertEquals(BigDecimal.ZERO, result);
451457
}
452458

453459
@Test
454460
public void getQuotaTariffValueToBeAppliedTestScriptResultIsNotBooleanNorNumericReturnZero() {
461+
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);
462+
455463
Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
456464
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
457465
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
458466
Mockito.doReturn("test").when(jsInterpreterMock).executeScript(Mockito.anyString());
459467

460-
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
468+
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);
461469

462470
Assert.assertEquals(BigDecimal.ZERO, result);
463471
}
@@ -477,10 +485,7 @@ public void getPresetVariablesTestHasTariffsWithActivationRuleReturnPresetVariab
477485

478486
@Test
479487
public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnZero() {
480-
List<QuotaTariffVO> tariffs = new ArrayList<>();
481-
tariffs.add(new QuotaTariffVO());
482-
tariffs.add(new QuotaTariffVO());
483-
tariffs.add(new QuotaTariffVO());
488+
List<QuotaTariffVO> tariffs = createTariffList();
484489

485490
Mockito.doReturn(false).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
486491
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
@@ -497,13 +502,10 @@ public void aggregateQuotaTariffsValuesTestTariffsIsEmptyReturnZero() {
497502

498503
@Test
499504
public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnAggregation() {
500-
List<QuotaTariffVO> tariffs = new ArrayList<>();
501-
tariffs.add(new QuotaTariffVO());
502-
tariffs.add(new QuotaTariffVO());
503-
tariffs.add(new QuotaTariffVO());
505+
List<QuotaTariffVO> tariffs = createTariffList();
504506

505507
Mockito.doReturn(true, false, true).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
506-
Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any());
508+
Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
507509
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
508510

509511
Assert.assertEquals(BigDecimal.TEN.multiply(new BigDecimal(2)), result);
@@ -528,4 +530,25 @@ public void persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsagesTestReturn
528530
Assert.assertEquals(quotaUsageVoMock1, result.get(0));
529531
Assert.assertEquals(quotaUsageVoMock2, result.get(1));
530532
}
533+
534+
private static List<QuotaTariffVO> createTariffList() {
535+
List<QuotaTariffVO> tariffs = new ArrayList<>();
536+
tariffs.add(new QuotaTariffVO());
537+
tariffs.add(new QuotaTariffVO());
538+
tariffs.add(new QuotaTariffVO());
539+
tariffs.forEach(quotaTariffVO -> quotaTariffVO.setPosition(1));
540+
return tariffs;
541+
}
542+
543+
private static List<Tariff> createLastAppliedTariffsPresetVariableList(int numberOfTariffs) {
544+
List<Tariff> lastTariffs = new ArrayList<>();
545+
for (int i = 0; i < numberOfTariffs; i++) {
546+
Tariff tariff = new Tariff();
547+
tariff.setId(String.valueOf(i));
548+
tariff.setValue(BigDecimal.valueOf(i));
549+
lastTariffs.add(tariff);
550+
}
551+
return lastTariffs;
552+
}
553+
531554
}

plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffCreateCmd.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public class QuotaTariffCreateCmd extends BaseCmd {
6868
ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS)
6969
private Date endDate;
7070

71+
@Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0")
72+
private Integer position;
73+
7174
@Override
7275
public void execute() {
7376
CallContext.current().setEventDetails(String.format("Tariff: %s, description: %s, value: %s", getName(), getDescription(), getValue()));
@@ -139,4 +142,13 @@ public void setEndDate(Date endDate) {
139142
public ApiCommandResourceType getApiResourceType() {
140143
return ApiCommandResourceType.QuotaTariff;
141144
}
145+
public Integer getPosition() {
146+
return position;
147+
}
148+
149+
public void setPosition(Integer position) {
150+
this.position = position;
151+
}
152+
153+
142154
}

plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaTariffUpdateCmd.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ public class QuotaTariffUpdateCmd extends BaseCmd {
6969
"value will be applied. Inform empty to remove the activation rule.", length = 65535, since = "4.18.0.0")
7070
private String activationRule;
7171

72+
@Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0")
73+
private Integer position;
74+
7275
public Integer getUsageType() {
7376
return usageType;
7477
}
@@ -130,4 +133,13 @@ public long getEntityOwnerId() {
130133
public ApiCommandResourceType getApiResourceType() {
131134
return ApiCommandResourceType.QuotaTariff;
132135
}
136+
137+
public Integer getPosition() {
138+
return position;
139+
}
140+
141+
public void setPosition(Integer position) {
142+
this.position = position;
143+
}
144+
133145
}

0 commit comments

Comments
 (0)