This is an automated email from the ASF dual-hosted git repository.

DaanHoogland 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 2032bbd7f67 Addition of the status filter in the backup listing API  
(#13254)
2032bbd7f67 is described below

commit 2032bbd7f678b4a224d8a7d107902496655f1b1e
Author: Gean Jair Silva <[email protected]>
AuthorDate: Fri Jul 3 11:58:39 2026 -0300

    Addition of the status filter in the backup listing API  (#13254)
    
    Co-authored-by: gean.silva <[email protected]>
    Co-authored-by: Gean Jair Silva <[email protected]>
    Co-authored-by: Bernardo De Marco Gonçalves <[email protected]>
---
 .../api/command/user/backup/ListBackupsCmd.java    | 10 ++++++
 .../cloudstack/backup/BackupManagerImpl.java       | 21 ++++++++++++
 ui/public/locales/en.json                          |  4 +++
 ui/public/locales/pt_BR.json                       |  5 ++-
 ui/src/components/view/SearchView.vue              | 40 +++++++++++++++++++++-
 ui/src/components/widgets/Status.vue               | 18 ++++++++++
 ui/src/config/section/storage.js                   |  2 +-
 7 files changed, 97 insertions(+), 3 deletions(-)

diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java
index fb9c92f433e..35cadd9f2e5 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java
@@ -89,6 +89,12 @@ public class ListBackupsCmd extends 
BaseListProjectAndAccountResourcesCmd {
             description = "list backups by backup offering")
     private Long backupOfferingId;
 
+    @Parameter(name = ApiConstants.STATUS,
+            type = CommandType.STRING,
+            since = "4.23.0",
+            description = "list backups by status")
+    private String backupStatus;
+
     @Parameter(name = ApiConstants.LIST_VM_DETAILS,
             type = CommandType.BOOLEAN,
             since = "4.21.0",
@@ -119,6 +125,10 @@ public class ListBackupsCmd extends 
BaseListProjectAndAccountResourcesCmd {
         return zoneId;
     }
 
+    public String getBackupStatus() {
+        return backupStatus;
+    }
+
     public Boolean getListVmDetails() {
         return listVmDetails;
     }
diff --git 
a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java 
b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
index c17c0b487f1..f9c98cb141b 100644
--- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
@@ -39,6 +39,7 @@ import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
 import com.cloud.utils.DomainHelper;
+import com.cloud.utils.EnumUtils;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.InternalIdentity;
@@ -256,6 +257,8 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
     private static Map<String, BackupProvider> backupProvidersMap = new 
HashMap<>();
     private List<BackupProvider> backupProviders;
 
+    private static final List<Backup.Status> INVALID_BACKUP_STATUS = 
List.of(Backup.Status.Expunged, Backup.Status.Removed);
+
     public AsyncJobDispatcher getAsyncJobDispatcher() {
         return asyncJobDispatcher;
     }
@@ -1084,6 +1087,20 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         }
     }
 
+    private Backup.Status validateBackupStatus(final String backupStatus) {
+        if (backupStatus == null) {
+            return null;
+        }
+
+        Backup.Status status = 
EnumUtils.getEnumIgnoreCase(Backup.Status.class, backupStatus);
+        if (status == null || INVALID_BACKUP_STATUS.contains(status)) {
+            throw new InvalidParameterValueException(String.format("Invalid 
backup status: %s. Valid values are: " +
+                    "Allocated, Queued, BackingUp, BackedUp, Error, Failed, 
Restoring.", backupStatus));
+        }
+
+        return status;
+    }
+
     @Override
     public Pair<List<Backup>, Integer> listBackups(final ListBackupsCmd cmd) {
         final Long id = cmd.getId();
@@ -1091,6 +1108,7 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         final String name = cmd.getName();
         final Long zoneId = cmd.getZoneId();
         final Long backupOfferingId = cmd.getBackupOfferingId();
+        final Backup.Status backupStatus = 
validateBackupStatus(cmd.getBackupStatus());
         final Account caller = CallContext.current().getCallingAccount();
         final String keyword = cmd.getKeyword();
         List<Long> permittedAccounts = new ArrayList<Long>();
@@ -1119,6 +1137,7 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
         sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ);
         sb.and("backupOfferingId", sb.entity().getBackupOfferingId(), 
SearchCriteria.Op.EQ);
+        sb.and("backupStatus", sb.entity().getStatus(), SearchCriteria.Op.EQ);
 
         if (keyword != null) {
             sb.and().op("keywordName", sb.entity().getName(), 
SearchCriteria.Op.LIKE);
@@ -1151,6 +1170,8 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
             sc.setParameters("backupOfferingId", backupOfferingId);
         }
 
+        sc.setParametersIfNotNull("backupStatus", backupStatus);
+
         if (keyword != null) {
             String keywordMatch = "%" + keyword + "%";
             sc.setParameters("keywordName", keywordMatch);
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index f93a7644611..c64ae4d7849 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -469,6 +469,8 @@
 "label.back.login": "Back to login",
 "label.backup": "Backup",
 "label.backups": "Backups",
+"label.backedup": "BackedUp",
+"label.backingup": "BackingUp",
 "label.backup.attach.restore": "Restore and attach backup volume",
 "label.backup.configure.schedule": "Configure Backup Schedule",
 "label.backup.offering.assign": "Assign Instance to backup offering",
@@ -2052,6 +2054,7 @@
 "label.purge.usage.records.error": "Failed while purging usage records",
 "label.purpose": "Purpose",
 "label.qostype": "QoS type",
+"label.queued": "Queued",
 "label.quickview": "Quick view",
 "label.quiescevm": "Quiesce Instance",
 "label.quiettime": "Quiet time (in sec)",
@@ -2210,6 +2213,7 @@
 "label.restartrequired": "Restart required",
 "label.restore": "Restore",
 "label.restore.volume.attach": "Restore volume and attach",
+"label.restoring": "Restoring",
 "label.use.backup.ip.address": "Use IP Addresses from Backup",
 "label.use.backup.ip.address.tooltip": "Use the same IP/MAC addresses as 
stored in the backup metadata. The command will error out if the IP/MAC 
addresses are not available",
 "label.review": "Review",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index c0d66280d7c..a022769a5b0 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -436,7 +436,8 @@
 "label.backup": "Backup",
 "label.backups": "Backups",
 "label.back.login": "Voltar à página de login",
-"label.backup": "Backups",
+"label.backedup": "Salvo",
+"label.backingup": "Salvando",
 "label.backup.attach.restore": "Restaurar e anexar volume de backup",
 "label.backuplimit": "Limite de backups",
 "label.backup.storage": "Armazenamento de backup",
@@ -1828,6 +1829,7 @@
 "label.purgeresources": "Limpar Recursos",
 "label.purpose": "Prop\u00f3sito",
 "label.qostype": "Tipo de QoS",
+"label.queued": "Enfileirado",
 "label.quickview": "Visualiza\u00e7\u00e3o r\u00e1pida",
 "label.quiescevm": "Quiesce VM",
 "label.quiettime": "Tempo em espera (em seg)",
@@ -1982,6 +1984,7 @@
 "label.restartrequired": "Reiniciar obrigat\u00f3rio",
 "label.restore": "Restaurar",
 "label.restore.volume.attach": "Restaurar volume e anex\u00e1-lo",
+"label.restoring": "Restaurando",
 "label.review": "Revisar",
 "label.role": "Fun\u00e7\u00e3o",
 "label.roleid": "Fun\u00e7\u00e3o",
diff --git a/ui/src/components/view/SearchView.vue 
b/ui/src/components/view/SearchView.vue
index 60809328929..227ee0a75fa 100644
--- a/ui/src/components/view/SearchView.vue
+++ b/ui/src/components/view/SearchView.vue
@@ -337,7 +337,7 @@ export default {
           'type', 'scope', 'managementserverid', 'serviceofferingid',
           'diskofferingid', 'networkid', 'usagetype', 'restartrequired', 
'gpuenabled',
           'displaynetwork', 'guestiptype', 'usersource', 'arch', 
'oscategoryid', 'templatetype', 'gpucardid', 'vgpuprofileid',
-          'extensionid', 'backupoffering', 'volumeid', 'virtualmachineid', 
'hsmprofileid', 'kmskeyid'].includes(item)
+          'extensionid', 'backupoffering', 'volumeid', 'virtualmachineid', 
'hsmprofileid', 'kmskeyid', 'status'].includes(item)
         ) {
           type = 'list'
         } else if (item === 'tags') {
@@ -530,6 +530,7 @@ export default {
       let extensionIndex = -1
       let hsmProfileIndex = -1
       let kmsKeyIndex = -1
+      let backupStatusIndex = -1
 
       if (arrayField.includes('type')) {
         if (this.$route.path === '/alert') {
@@ -687,6 +688,11 @@ export default {
         promises.push(await this.fetchKMSKeys(searchKeyword))
       }
 
+      if (arrayField.includes('status')) {
+        backupStatusIndex = this.fields.findIndex(item => item.name === 
'status')
+        this.fields[backupStatusIndex].opts = this.fetchAvailableBackupStatus()
+      }
+
       Promise.all(promises).then(response => {
         if (typeIndex > -1) {
           const types = response.filter(item => item.type === 'type')
@@ -1662,6 +1668,38 @@ export default {
         })
       })
     },
+    fetchAvailableBackupStatus () {
+      return [
+        {
+          id: 'Allocated',
+          name: 'label.allocated'
+        },
+        {
+          id: 'Queued',
+          name: 'label.queued'
+        },
+        {
+          id: 'BackingUp',
+          name: 'label.backingup'
+        },
+        {
+          id: 'BackedUp',
+          name: 'label.backedup'
+        },
+        {
+          id: 'Error',
+          name: 'label.error'
+        },
+        {
+          id: 'Failed',
+          name: 'label.failed'
+        },
+        {
+          id: 'Restoring',
+          name: 'label.restoring'
+        }
+      ]
+    },
     onSearch (value) {
       this.paramsFilter = {}
       this.searchQuery = value
diff --git a/ui/src/components/widgets/Status.vue 
b/ui/src/components/widgets/Status.vue
index 6b9f71b5de8..1f6a1183e4a 100644
--- a/ui/src/components/widgets/Status.vue
+++ b/ui/src/components/widgets/Status.vue
@@ -106,6 +106,24 @@ export default {
           case 'no':
             state = this.$t('label.no')
             break
+          case 'backedup':
+            state = this.$t('label.backedup')
+            break
+          case 'backingup':
+            state = this.$t('label.backingup')
+            break
+          case 'allocated':
+            state = this.$t('label.allocated')
+            break
+          case 'queued':
+            state = this.$t('label.queued')
+            break
+          case 'restoring':
+            state = this.$t('label.restoring')
+            break
+          case 'failed':
+            state = this.$t('label.failed')
+            break
         }
         return state.charAt(0).toUpperCase() + state.slice(1)
       }
diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js
index 14f3cf821dc..e3cd875a6b8 100644
--- a/ui/src/config/section/storage.js
+++ b/ui/src/config/section/storage.js
@@ -495,7 +495,7 @@ export default {
       columns: ['name', 'status', 'size', 'virtualsize', 'virtualmachinename', 
'backupofferingname', 'intervaltype', 'type', 'created', 'account', 'domain', 
'zone'],
       details: ['name', 'description', 'virtualmachinename', 'id', 
'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 
'backupofferingname', 'zone', 'account', 'domain', 'created'],
       searchFilters: () => {
-        var filters = ['name', 'zoneid', 'domainid', 'account', 
'backupofferingid']
+        var filters = ['name', 'zoneid', 'domainid', 'account', 
'backupofferingid', 'status']
         return filters
       },
       tabs: [

Reply via email to