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());


Reply via email to