This is an automated email from the ASF dual-hosted git repository. bryan pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push: new 1cf1786ebb8 [Quota] Add API to list preset variables (#8372) 1cf1786ebb8 is described below commit 1cf1786ebb8ff7de5024d32a78019d2c13934844 Author: Bryan Lima <42067040+bryanml...@users.noreply.github.com> AuthorDate: Thu Jun 13 16:49:14 2024 -0300 [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 <bernardomg2...@gmail.com> --------- Co-authored-by: Bernardo De Marco Gonçalves <bernardomg2...@gmail.com> --- .../activationrule/presetvariables/Account.java | 4 +- .../presetvariables/BackupOffering.java | 1 + .../presetvariables/ComputeOffering.java | 1 + .../presetvariables/ComputingResources.java | 5 + .../activationrule/presetvariables/Domain.java | 1 + .../presetvariables/GenericPresetVariable.java | 4 + .../quota/activationrule/presetvariables/Host.java | 2 + .../presetvariables/PresetVariableDefinition.java | 42 +++++++ .../presetvariables/PresetVariables.java | 11 ++ .../quota/activationrule/presetvariables/Role.java | 1 + .../activationrule/presetvariables/Storage.java | 4 + .../activationrule/presetvariables/Value.java | 50 ++++++++ .../cloudstack/quota/constant/QuotaTypes.java | 10 ++ .../activationrule/presetvariables/ValueTest.java | 14 +++ .../api/command/QuotaPresetVariablesListCmd.java | 66 +++++++++++ .../response/QuotaPresetVariablesItemResponse.java | 47 ++++++++ .../api/response/QuotaResponseBuilder.java | 8 ++ .../api/response/QuotaResponseBuilderImpl.java | 128 +++++++++++++++++++++ .../apache/cloudstack/quota/QuotaServiceImpl.java | 2 + .../api/response/QuotaResponseBuilderImplTest.java | 45 +++++++- 20 files changed, 444 insertions(+), 2 deletions(-) diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java index c0b1f762f70..37c90ab0bcd 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Account.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java index 457e71a141f..d8457d294ec 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/BackupOffering.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java index b42c32a584e..1d294276d47 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputeOffering.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java index d4f335b081c..9c86d2d6e0c 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/ComputingResources.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java index 01b702feb1a..6d83da4cd8f 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Domain.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java index b081e57611f..f59f23abdc1 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/GenericPresetVariable.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java index fef3e4376dc..4a0fd2f5a07 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Host.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableDefinition.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableDefinition.java new file mode 100644 index 00000000000..0e10a8af9d1 --- /dev/null +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariableDefinition.java @@ -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 ""; +} diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java index 2fb6e1ac131..b27bf589c16 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/PresetVariables.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java index fc4716fc309..3f953b3a4ff 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Role.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java index 6be1dfb025a..9b6cfb31092 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Storage.java @@ -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() { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java index a1dc7b3c1bb..d87146d8798 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/activationrule/presetvariables/Value.java @@ -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; diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java index 3ed162b2ba1..947183577a8 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java @@ -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"); + } } diff --git a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java index 4d0162b33c9..bad33da8836 100644 --- a/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java +++ b/framework/quota/src/test/java/org/apache/cloudstack/quota/activationrule/presetvariables/ValueTest.java @@ -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(); diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaPresetVariablesListCmd.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaPresetVariablesListCmd.java new file mode 100644 index 00000000000..8de16dd2741 --- /dev/null +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaPresetVariablesListCmd.java @@ -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; + } +} diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaPresetVariablesItemResponse.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaPresetVariablesItemResponse.java new file mode 100644 index 00000000000..a1b80fd94eb --- /dev/null +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaPresetVariablesItemResponse.java @@ -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; + } +} diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java index 57aa04e00fa..ecbb809b60b 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java @@ -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); diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java index 94f821828ab..7b5667ac13d 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java @@ -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); diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java index 4bc41233096..17fa7bd8425 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java @@ -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; } diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java index 664863a1b90..6bca4ea85bb 100644 --- a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java +++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java @@ -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());