[Quota] Add API to list preset variables (#8372)

* Add API for listing Quota preset variables

* Add new line at EOF

* Address review

* Remove usage types

* Remove usage types from quotatypes

* Remove unused imports

* Add space for preset variable definition description

Co-authored-by: Bernardo De Marco Gonçalves <bernardomg2004@gmail.com>

---------

Co-authored-by: Bernardo De Marco Gonçalves <bernardomg2004@gmail.com>
This commit is contained in:
Bryan Lima 2024-06-13 16:49:14 -03:00 committed by GitHub
parent cb48202b34
commit 1cf1786ebb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 444 additions and 2 deletions

View File

@ -17,7 +17,9 @@
package org.apache.cloudstack.quota.activationrule.presetvariables;
public class Account extends GenericPresetVariable{
public class Account extends GenericPresetVariable {
@PresetVariableDefinition(description = "Role of the account. This field will not exist if the account is a project.")
private Role role;
public Role getRole() {

View File

@ -20,6 +20,7 @@
package org.apache.cloudstack.quota.activationrule.presetvariables;
public class BackupOffering extends GenericPresetVariable {
@PresetVariableDefinition(description = "External ID of the backup offering that generated the backup.")
private String externalId;
public String getExternalId() {

View File

@ -18,6 +18,7 @@
package org.apache.cloudstack.quota.activationrule.presetvariables;
public class ComputeOffering extends GenericPresetVariable {
@PresetVariableDefinition(description = "A boolean informing if the compute offering is customized or not.")
private boolean customized;
public boolean isCustomized() {

View File

@ -21,8 +21,13 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class ComputingResources {
@PresetVariableDefinition(description = "Current VM's memory (in MiB).")
private Integer memory;
@PresetVariableDefinition(description = "Current VM's vCPUs.")
private Integer cpuNumber;
@PresetVariableDefinition(description = "Current VM's CPU speed (in MHz).")
private Integer cpuSpeed;
public Integer getMemory() {

View File

@ -18,6 +18,7 @@
package org.apache.cloudstack.quota.activationrule.presetvariables;
public class Domain extends GenericPresetVariable {
@PresetVariableDefinition(description = "Path of the domain owner of the resource.")
private String path;
public String getPath() {

View File

@ -23,8 +23,12 @@ import java.util.Set;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
public class GenericPresetVariable {
@PresetVariableDefinition(description = "ID of the resource.")
private String id;
@PresetVariableDefinition(description = "Name of the resource.")
private String name;
protected transient Set<String> fieldNamesToIncludeInToString = new HashSet<>();
public String getId() {

View File

@ -20,8 +20,10 @@ package org.apache.cloudstack.quota.activationrule.presetvariables;
import java.util.List;
public class Host extends GenericPresetVariable {
@PresetVariableDefinition(description = "List of tags of the host where the VM is running (i.e.: [\"a\", \"b\"]).")
private List<String> tags;
@PresetVariableDefinition(description = "Whether the tag is a rule interpreted in JavaScript.")
private Boolean isTagARule;
public List<String> getTags() {

View File

@ -0,0 +1,42 @@
// 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
// 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.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Describes the preset variable and indicates to which Quota usage types it is loaded.
*/
@Target(FIELD)
@Retention(RUNTIME)
public @interface PresetVariableDefinition {
/**
* An array indicating for which Quota usage types the preset variable is loaded.
* @return an array with the usage types for which the preset variable is loaded.
*/
int[] supportedTypes() default 0;
/**
* A {@link String} describing the preset variable.
* @return the description of the preset variable.
*/
String description() default "";
}

View File

@ -19,11 +19,22 @@ package org.apache.cloudstack.quota.activationrule.presetvariables;
public class PresetVariables {
@PresetVariableDefinition(description = "Account owner of the resource.")
private Account account;
@PresetVariableDefinition(description = "Domain owner of the resource.")
private Domain domain;
@PresetVariableDefinition(description = "Project owner of the resource. This field will not exist if the resource belongs to an account.")
private GenericPresetVariable project;
@PresetVariableDefinition(description = "Type of the record used. Examples for this are: VirtualMachine, DomainRouter, SourceNat, KVM.")
private String resourceType;
@PresetVariableDefinition(description = "Data related to the resource being processed.")
private Value value;
@PresetVariableDefinition(description = "Zone where the resource is.")
private GenericPresetVariable zone;
public Account getAccount() {

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.quota.activationrule.presetvariables;
import org.apache.cloudstack.acl.RoleType;
public class Role extends GenericPresetVariable {
@PresetVariableDefinition(description = "Role type of the resource's owner.")
private RoleType type;
public RoleType getType() {

View File

@ -22,9 +22,13 @@ import java.util.List;
import com.cloud.storage.ScopeType;
public class Storage extends GenericPresetVariable {
@PresetVariableDefinition(description = "List of string representing the tags of the storage where the volume is (i.e.: [\"a\", \"b\"]).")
private List<String> tags;
@PresetVariableDefinition(description = "Whether the tag is a rule interpreted in JavaScript. Applicable only for primary storages.")
private Boolean isTagARule;
@PresetVariableDefinition(description = "Scope of the storage where the volume is. Values can be: ZONE, CLUSTER or HOST. Applicable only for primary storages.")
private ScopeType scope;
public List<String> getTags() {

View File

@ -23,25 +23,75 @@ import java.util.Map;
import com.cloud.storage.Snapshot;
import com.cloud.storage.Storage.ProvisioningType;
import com.cloud.vm.snapshot.VMSnapshot;
import org.apache.cloudstack.quota.constant.QuotaTypes;
public class Value extends GenericPresetVariable {
@PresetVariableDefinition(description = "ID of the resource.", supportedTypes = {QuotaTypes.ALLOCATED_VM, QuotaTypes.RUNNING_VM, QuotaTypes.VOLUME, QuotaTypes.TEMPLATE,
QuotaTypes.ISO, QuotaTypes.SNAPSHOT, QuotaTypes.NETWORK_OFFERING, QuotaTypes.VM_SNAPSHOT})
private String id;
@PresetVariableDefinition(description = "Name of the resource.", supportedTypes = {QuotaTypes.ALLOCATED_VM, QuotaTypes.RUNNING_VM, QuotaTypes.VOLUME, QuotaTypes.TEMPLATE,
QuotaTypes.ISO, QuotaTypes.SNAPSHOT, QuotaTypes.NETWORK_OFFERING, QuotaTypes.VM_SNAPSHOT})
private String name;
@PresetVariableDefinition(description = "Host where the VM is running.", supportedTypes = {QuotaTypes.RUNNING_VM})
private Host host;
@PresetVariableDefinition(description = "OS of the VM/template.", supportedTypes = {QuotaTypes.RUNNING_VM, QuotaTypes.ALLOCATED_VM, QuotaTypes.TEMPLATE, QuotaTypes.ISO})
private String osName;
@PresetVariableDefinition(description = "A list of resources of the account between the start and end date of the usage record being calculated " +
"(i.e.: [{zoneId: ..., domainId:...}]).")
private List<Resource> accountResources;
@PresetVariableDefinition(supportedTypes = {QuotaTypes.ALLOCATED_VM, QuotaTypes.RUNNING_VM, QuotaTypes.VOLUME, QuotaTypes.TEMPLATE, QuotaTypes.ISO, QuotaTypes.SNAPSHOT,
QuotaTypes.VM_SNAPSHOT}, description = "List of tags of the resource in the format key:value (i.e.: {\"a\":\"b\", \"c\":\"d\"}).")
private Map<String, String> tags;
@PresetVariableDefinition(description = "Tag of the network offering.", supportedTypes = {QuotaTypes.NETWORK_OFFERING})
private String tag;
@PresetVariableDefinition(description = "Size of the resource (in MiB).", supportedTypes = {QuotaTypes.TEMPLATE, QuotaTypes.ISO, QuotaTypes.VOLUME, QuotaTypes.SNAPSHOT,
QuotaTypes.BACKUP})
private Long size;
@PresetVariableDefinition(description = "Virtual size of the backup.", supportedTypes = {QuotaTypes.BACKUP})
private Long virtualSize;
@PresetVariableDefinition(description = "Provisioning type of the resource. Values can be: thin, sparse or fat.", supportedTypes = {QuotaTypes.VOLUME})
private ProvisioningType provisioningType;
@PresetVariableDefinition(description = "Type of the snapshot. Values can be: MANUAL, RECURRING, HOURLY, DAILY, WEEKLY and MONTHLY.", supportedTypes = {QuotaTypes.SNAPSHOT})
private Snapshot.Type snapshotType;
@PresetVariableDefinition(description = "Type of the VM snapshot. Values can be: Disk or DiskAndMemory.", supportedTypes = {QuotaTypes.VM_SNAPSHOT})
private VMSnapshot.Type vmSnapshotType;
@PresetVariableDefinition(description = "Computing offering of the VM.", supportedTypes = {QuotaTypes.RUNNING_VM, QuotaTypes.ALLOCATED_VM})
private ComputeOffering computeOffering;
@PresetVariableDefinition(description = "Template/ISO with which the VM was created.", supportedTypes = {QuotaTypes.RUNNING_VM, QuotaTypes.ALLOCATED_VM})
private GenericPresetVariable template;
@PresetVariableDefinition(description = "Disk offering of the volume.", supportedTypes = {QuotaTypes.VOLUME})
private GenericPresetVariable diskOffering;
@PresetVariableDefinition(description = "Storage where the volume or snapshot is. While handling with snapshots, this value can be from the primary storage if the global " +
"setting 'snapshot.backup.to.secondary' is false, otherwise it will be from secondary storage.", supportedTypes = {QuotaTypes.VOLUME, QuotaTypes.SNAPSHOT})
private Storage storage;
@PresetVariableDefinition(description = "Computing resources consumed by the VM.", supportedTypes = {QuotaTypes.RUNNING_VM})
private ComputingResources computingResources;
@PresetVariableDefinition(description = "Backup offering of the backup.", supportedTypes = {QuotaTypes.BACKUP})
private BackupOffering backupOffering;
@PresetVariableDefinition(description = "The hypervisor where the resource was deployed. Values can be: XenServer, KVM, VMware, Hyperv, BareMetal, Ovm, Ovm3 and LXC.",
supportedTypes = {QuotaTypes.RUNNING_VM, QuotaTypes.ALLOCATED_VM, QuotaTypes.VM_SNAPSHOT, QuotaTypes.SNAPSHOT})
private String hypervisorType;
@PresetVariableDefinition(description = "The volume format. Values can be: RAW, VHD, VHDX, OVA and QCOW2.", supportedTypes = {QuotaTypes.VOLUME, QuotaTypes.VOLUME_SECONDARY})
private String volumeFormat;
private String state;

View File

@ -22,6 +22,7 @@ import java.util.Map;
import org.apache.cloudstack.usage.UsageTypes;
import org.apache.cloudstack.usage.UsageUnitTypes;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
public class QuotaTypes extends UsageTypes {
private final Integer quotaType;
@ -100,4 +101,13 @@ public class QuotaTypes extends UsageTypes {
}
return null;
}
static public QuotaTypes getQuotaType(int quotaType) {
return quotaTypeMap.get(quotaType);
}
@Override
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "quotaType", "quotaName");
}
}

View File

@ -25,6 +25,20 @@ import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ValueTest {
@Test
public void setIdTestAddFieldIdToCollection() {
Value variable = new Value();
variable.setId(null);
Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("id"));
}
@Test
public void setNameTestAddFieldNameToCollection() {
Value variable = new Value();
variable.setName(null);
Assert.assertTrue(variable.fieldNamesToIncludeInToString.contains("name"));
}
@Test
public void setHostTestAddFieldHostToCollection() {
Value variable = new Value();

View File

@ -0,0 +1,66 @@
//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.api.command;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.QuotaPresetVariablesItemResponse;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import javax.inject.Inject;
import java.util.List;
@APICommand(name = "quotaPresetVariablesList", responseObject = QuotaPresetVariablesItemResponse.class, description = "List the preset variables available for using in the " +
"Quota tariff activation rules given the usage type.", since = "4.20", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class QuotaPresetVariablesListCmd extends BaseCmd {
@Inject
QuotaResponseBuilder quotaResponseBuilder;
@Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = true, description = "The usage type for which the preset variables will be retrieved.")
private Integer quotaType;
@Override
public void execute() {
List<QuotaPresetVariablesItemResponse> responses = quotaResponseBuilder.listQuotaPresetVariables(this);
ListResponse<QuotaPresetVariablesItemResponse> listResponse = new ListResponse<>();
listResponse.setResponses(responses);
listResponse.setResponseName(getCommandName());
setResponseObject(listResponse);
}
public QuotaTypes getQuotaType() {
QuotaTypes quotaTypes = QuotaTypes.getQuotaType(quotaType);
if (quotaTypes == null) {
throw new InvalidParameterValueException(String.format("Usage type not found for value [%s].", quotaType));
}
return quotaTypes;
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -0,0 +1,47 @@
//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.api.response;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.BaseResponse;
public class QuotaPresetVariablesItemResponse extends BaseResponse {
@SerializedName("variable")
@Param(description = "variable")
private String variable;
@SerializedName("description")
@Param(description = "description")
private String description;
public QuotaPresetVariablesItemResponse() {
super("variables");
}
public void setVariable(String variable) {
this.variable = variable;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -20,6 +20,7 @@ import org.apache.cloudstack.api.command.QuotaBalanceCmd;
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
@ -72,6 +73,13 @@ public interface QuotaResponseBuilder {
boolean deleteQuotaTariff(String quotaTariffUuid);
/**
* Lists the preset variables for the usage type informed in the command.
* @param cmd used to retrieve the Quota usage type parameter.
* @return the response consisting of a {@link List} of the preset variables and their descriptions.
*/
List<QuotaPresetVariablesItemResponse> listQuotaPresetVariables(QuotaPresetVariablesListCmd cmd);
Pair<QuotaEmailConfigurationVO, Double> configureQuotaEmail(QuotaConfigureEmailCmd cmd);
QuotaConfigureEmailResponse createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO quotaEmailConfigurationVO, Double minBalance, long accountId);

View File

@ -16,11 +16,15 @@
//under the License.
package org.apache.cloudstack.api.response;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
@ -31,6 +35,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
@ -41,6 +46,7 @@ import org.apache.cloudstack.api.command.QuotaBalanceCmd;
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
@ -50,6 +56,11 @@ import org.apache.cloudstack.quota.QuotaManager;
import org.apache.cloudstack.quota.QuotaManagerImpl;
import org.apache.cloudstack.quota.QuotaService;
import org.apache.cloudstack.quota.QuotaStatement;
import org.apache.cloudstack.quota.activationrule.presetvariables.ComputingResources;
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableDefinition;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
@ -67,6 +78,8 @@ import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.springframework.stereotype.Component;
@ -119,6 +132,8 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@Inject
private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class};
@Override
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
final QuotaTariffResponse response = new QuotaTariffResponse();
@ -680,6 +695,119 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
return _quotaTariffDao.updateQuotaTariff(quotaTariff);
}
@Override
public List<QuotaPresetVariablesItemResponse> listQuotaPresetVariables(QuotaPresetVariablesListCmd cmd) {
List<QuotaPresetVariablesItemResponse> response;
List<Pair<String, String>> variables = new ArrayList<>();
QuotaTypes quotaType = cmd.getQuotaType();
addAllPresetVariables(PresetVariables.class, quotaType, variables, null);
response = createQuotaPresetVariablesResponse(variables);
return response;
}
/**
* Adds all preset variables for the given quota type. It recursively finds all presets variables for the given {@link Class} and puts it in a {@link List}. Each item in the
* list is a {@link Pair} that consists of the variable name and its description.
*
* @param clazz used to find the non-transient fields. If it is equal to the {@link Value} class, then it only gets the declared fields, otherwise, it gets all fields,
* including its parent's fields.
* @param quotaType used to check if the field supports the quota resource type. It uses the annotation method {@link PresetVariableDefinition#supportedTypes()} for this
* verification.
* @param variables the {@link List} which contains the {@link Pair} of the preset variable and its description.
* @param recursiveVariableName {@link String} used for recursively building the preset variable string.
*/
public void addAllPresetVariables(Class<?> clazz, QuotaTypes quotaType, List<Pair<String, String>> variables, String recursiveVariableName) {
Field[] allFields = Value.class.equals(clazz) ? clazz.getDeclaredFields() : FieldUtils.getAllFields(clazz);
List<Field> fieldsNonTransients = Arrays.stream(allFields).filter(field -> !Modifier.isTransient(field.getModifiers())).collect(Collectors.toList());
for (Field field : fieldsNonTransients) {
PresetVariableDefinition presetVariableDefinitionAnnotation = field.getAnnotation(PresetVariableDefinition.class);
Class<?> fieldClass = getClassOfField(field);
String presetVariableName = field.getName();
if (presetVariableDefinitionAnnotation == null) {
continue;
}
if (StringUtils.isNotEmpty(recursiveVariableName)) {
presetVariableName = String.format("%s.%s", recursiveVariableName, field.getName());
}
filterSupportedTypes(variables, quotaType, presetVariableDefinitionAnnotation, fieldClass, presetVariableName);
}
}
/**
* Returns the class of the {@link Field} depending on its type. This method is required for retrieving the Class of Generic Types, i.e. {@link List}.
*/
protected Class<?> getClassOfField(Field field){
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
return (Class<?>) genericType.getActualTypeArguments()[0];
}
return field.getType();
}
/**
* Checks if the {@link PresetVariableDefinition} supports the given {@link QuotaTypes}. If it supports it, it adds the preset variable to the {@link List} recursively
* if it is from the one of the classes in the {@link QuotaResponseBuilderImpl#assignableClasses} array or directly if not.
*
* @param variables {@link List} of the {@link Pair} of the preset variable and its description.
* @param quotaType the given {@link QuotaTypes} to filter.
* @param presetVariableDefinitionAnnotation used to check if the quotaType is supported.
* @param fieldClass class of the field used to verify if it is from the {@link GenericPresetVariable} or {@link ComputingResources} classes. If it is, then it calls
* {@link QuotaResponseBuilderImpl#addAllPresetVariables(Class, QuotaTypes, List, String)} to add the preset variable. Otherwise, the {@link Pair} is
* added directly to the variables {@link List}.
* @param presetVariableName {@link String} that contains the recursive created preset variable name.
*/
public void filterSupportedTypes(List<Pair<String, String>> variables, QuotaTypes quotaType, PresetVariableDefinition presetVariableDefinitionAnnotation, Class<?> fieldClass,
String presetVariableName) {
if (Arrays.stream(presetVariableDefinitionAnnotation.supportedTypes()).noneMatch(supportedType ->
supportedType == quotaType.getQuotaType() || supportedType == 0)) {
return;
}
String presetVariableDescription = presetVariableDefinitionAnnotation.description();
Pair<String, String> pair = new Pair<>(presetVariableName, presetVariableDescription);
variables.add(pair);
if (isRecursivePresetVariable(fieldClass)) {
addAllPresetVariables(fieldClass, quotaType, variables, presetVariableName);
}
}
/**
* Returns true if the {@link Class} of the {@link Field} is from one of the classes in the array {@link QuotaResponseBuilderImpl#assignableClasses}, i.e., it is a recursive
* {@link PresetVariables}, returns false otherwise.
*/
private boolean isRecursivePresetVariable(Class<?> fieldClass) {
for (Class<?> clazz : assignableClasses) {
if (clazz.isAssignableFrom(fieldClass)) {
return true;
}
}
return false;
}
public List<QuotaPresetVariablesItemResponse> createQuotaPresetVariablesResponse(List<Pair<String, String>> variables) {
final List<QuotaPresetVariablesItemResponse> responses = new ArrayList<>();
for (Pair<String, String> variable : variables) {
responses.add(createPresetVariablesItemResponse(variable));
}
return responses;
}
public QuotaPresetVariablesItemResponse createPresetVariablesItemResponse(Pair<String, String> variable) {
QuotaPresetVariablesItemResponse response = new QuotaPresetVariablesItemResponse();
response.setVariable(variable.first());
response.setDescription(variable.second());
return response;
}
@Override
public Pair<QuotaEmailConfigurationVO, Double> configureQuotaEmail(QuotaConfigureEmailCmd cmd) {
validateQuotaConfigureEmailCmdParameters(cmd);

View File

@ -33,6 +33,7 @@ import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaEnabledCmd;
import org.apache.cloudstack.api.command.QuotaListEmailConfigurationCmd;
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
@ -119,6 +120,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
cmdList.add(QuotaTariffDeleteCmd.class);
cmdList.add(QuotaConfigureEmailCmd.class);
cmdList.add(QuotaListEmailConfigurationCmd.class);
cmdList.add(QuotaPresetVariablesListCmd.class);
return cmdList;
}

View File

@ -29,6 +29,7 @@ import java.util.function.Consumer;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.utils.Pair;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
@ -36,6 +37,9 @@ import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.quota.QuotaService;
import org.apache.cloudstack.quota.QuotaStatement;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableDefinition;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
@ -419,6 +423,46 @@ public class QuotaResponseBuilderImplTest extends TestCase {
assertTrue(quotaSummaryResponse.getQuotaEnabled());
}
@Test
public void filterSupportedTypesTestReturnWhenQuotaTypeDoesNotMatch() throws NoSuchFieldException {
List<Pair<String, String>> variables = new ArrayList<>();
Class<?> clazz = Value.class;
PresetVariableDefinition presetVariableDefinitionAnnotation = clazz.getDeclaredField("host").getAnnotation(PresetVariableDefinition.class);
QuotaTypes quotaType = QuotaTypes.getQuotaType(QuotaTypes.NETWORK_OFFERING);
int expectedVariablesSize = 0;
quotaResponseBuilderSpy.filterSupportedTypes(variables, quotaType, presetVariableDefinitionAnnotation, clazz, null);
assertEquals(expectedVariablesSize, variables.size());
}
@Test
public void filterSupportedTypesTestAddPresetVariableWhenClassIsNotInstanceOfGenericPresetVariableAndComputingResource() throws NoSuchFieldException {
List<Pair<String, String>> variables = new ArrayList<>();
Class<?> clazz = PresetVariables.class;
PresetVariableDefinition presetVariableDefinitionAnnotation = clazz.getDeclaredField("resourceType").getAnnotation(PresetVariableDefinition.class);
QuotaTypes quotaType = QuotaTypes.getQuotaType(QuotaTypes.NETWORK_OFFERING);
int expectedVariablesSize = 1;
String expectedVariableName = "variable.name";
quotaResponseBuilderSpy.filterSupportedTypes(variables, quotaType, presetVariableDefinitionAnnotation, clazz, "variable.name");
assertEquals(expectedVariablesSize, variables.size());
assertEquals(expectedVariableName, variables.get(0).first());
}
@Test
public void filterSupportedTypesTestCallRecursiveMethodWhenIsGenericPresetVariableClassOrComputingResourceClass() throws NoSuchFieldException {
List<Pair<String, String>> variables = new ArrayList<>();
Class<?> clazz = Value.class;
PresetVariableDefinition presetVariableDefinitionAnnotation = clazz.getDeclaredField("storage").getAnnotation(PresetVariableDefinition.class);
QuotaTypes quotaType = QuotaTypes.getQuotaType(QuotaTypes.VOLUME);
quotaResponseBuilderSpy.filterSupportedTypes(variables, quotaType, presetVariableDefinitionAnnotation, clazz, "variable.name");
Mockito.verify(quotaResponseBuilderSpy, Mockito.atLeastOnce()).addAllPresetVariables(Mockito.any(), Mockito.any(QuotaTypes.class), Mockito.anyList(),
Mockito.anyString());
}
@Test (expected = InvalidParameterValueException.class)
public void validateQuotaConfigureEmailCmdParametersTestNullQuotaAccount() {
@ -442,7 +486,6 @@ public class QuotaResponseBuilderImplTest extends TestCase {
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
}
@Test
public void validateQuotaConfigureEmailCmdParametersTestNullTemplateName() {
Mockito.doReturn(quotaAccountVOMock).when(quotaAccountDaoMock).findByIdQuotaAccount(Mockito.any());