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: [