From 8913e4bdcba456c7b73332dc360945b6f158c93c Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 20 Jan 2026 00:34:42 +0530 Subject: [PATCH 1/2] Show parent snapshot (along with the chain size) for incremental snapshots --- .../command/user/config/ListCapabilitiesCmd.java | 1 + .../api/response/CapabilitiesResponse.java | 8 ++++++++ .../api/response/SnapshotResponse.java | 16 ++++++++++++++++ .../cloud/api/query/dao/SnapshotJoinDaoImpl.java | 2 ++ .../com/cloud/server/ManagementServerImpl.java | 2 ++ ui/src/config/section/storage.js | 9 +++++++-- 6 files changed, 36 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java index ed1bd7b063b2..947467a6f5e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java @@ -51,6 +51,7 @@ public void execute() { response.setDiskOffMaxSize((Long)capabilities.get("customDiskOffMaxSize")); response.setRegionSecondaryEnabled((Boolean)capabilities.get("regionSecondaryEnabled")); response.setKVMSnapshotEnabled((Boolean)capabilities.get("KVMSnapshotEnabled")); + response.setSnapshotShowChainSize((Boolean)capabilities.get("SnapshotShowChainSize")); response.setAllowUserViewDestroyedVM((Boolean)capabilities.get("allowUserViewDestroyedVM")); response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM")); response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume")); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index 930d1a50de0f..d8b6384216d6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -73,6 +73,10 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "True if Snapshot is supported for KVM host, false otherwise") private boolean kvmSnapshotEnabled; + @SerializedName("snapshotshowchainsize") + @Param(description = "True to show the parent and chain size (sum of physical size of snapshot and all its parents) for incremental snapshots", since = "4.22.1") + private boolean snapshotShowChainSize; + @SerializedName("apilimitmax") @Param(description = "Max allowed number of api requests within the specified interval") private Integer apiLimitMax; @@ -197,6 +201,10 @@ public void setKVMSnapshotEnabled(boolean kvmSnapshotEnabled) { this.kvmSnapshotEnabled = kvmSnapshotEnabled; } + public void setSnapshotShowChainSize(boolean snapshotShowChainSize) { + this.snapshotShowChainSize = snapshotShowChainSize; + } + public void setApiLimitInterval(Integer apiLimitInterval) { this.apiLimitInterval = apiLimitInterval; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java index 827a55b18754..3db6fd87ed59 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java @@ -155,6 +155,14 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements @Param(description = "download progress of a snapshot", since = "4.19.0") private Map downloadDetails; + @SerializedName("parent") + @Param(description = "The parent ID of the Snapshot", since = "4.22.1") + private String parent; + + @SerializedName("parentname") + @Param(description = "The parent name of the Snapshot", since = "4.22.1") + private String parentName; + public SnapshotResponse() { tags = new LinkedHashSet(); } @@ -313,4 +321,12 @@ public void setDatastoreType(String datastoreType) { public void setDownloadDetails(Map downloadDetails) { this.downloadDetails = downloadDetails; } + + public void setParent(String parent) { + this.parent = parent; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } } diff --git a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java index 9ea14edf2b73..0b0d9b269f3e 100644 --- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java @@ -109,6 +109,8 @@ private void setSnapshotInfoDetailsInResponse(SnapshotJoinVO snapshot, SnapshotR if (showChainSize && snapshotInfo.getParent() != null) { long chainSize = calculateChainSize(snapshotInfo); snapshotResponse.setChainSize(chainSize); + snapshotResponse.setParent(snapshotInfo.getParent().getUuid()); + snapshotResponse.setParentName(snapshotInfo.getParent().getName()); } } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 47dcf60eb32d..16750076e4a6 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -4799,6 +4799,7 @@ public Map listCapabilities(final ListCapabilitiesCmd cmd) { final long diskOffMinSize = VolumeOrchestrationService.CustomDiskOfferingMinSize.value(); final long diskOffMaxSize = VolumeOrchestrationService.CustomDiskOfferingMaxSize.value(); final boolean KVMSnapshotEnabled = SnapshotManager.KVMSnapshotEnabled.value(); + final boolean SnapshotShowChainSize = SnapshotManager.snapshotShowChainSize.value(); final boolean userPublicTemplateEnabled = TemplateManager.AllowPublicUserTemplates.valueIn(caller.getId()); @@ -4839,6 +4840,7 @@ public Map listCapabilities(final ListCapabilitiesCmd cmd) { capabilities.put("customDiskOffMaxSize", diskOffMaxSize); capabilities.put("regionSecondaryEnabled", regionSecondaryEnabled); capabilities.put("KVMSnapshotEnabled", KVMSnapshotEnabled); + capabilities.put("SnapshotShowChainSize", SnapshotShowChainSize); capabilities.put("allowUserViewDestroyedVM", allowUserViewDestroyedVM); capabilities.put("allowUserExpungeRecoverVM", allowUserExpungeRecoverVM); capabilities.put("allowUserExpungeRecoverVolume", allowUserExpungeRecoverVolume); diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 41875ec4db53..03a78282b321 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -311,7 +311,12 @@ export default { permission: ['listSnapshots'], resourceType: 'Snapshot', columns: () => { - var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize', 'created'] + var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize'] + if (store.getters.features.snapshotshowchainsize) { + fields.push('chainsize') + fields.push('parentname') + } + fields.push('created') if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('account') if (store.getters.listAllProjects) { @@ -324,7 +329,7 @@ export default { fields.push('zonename') return fields }, - details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'chainsize', 'account', 'domain', 'created'], + details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'chainsize', 'parentname', 'account', 'domain', 'created'], tabs: [ { name: 'details', From bd98e925855b6b29ea577253eec38300ac4a4265 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 20 Jan 2026 01:10:58 +0530 Subject: [PATCH 2/2] review --- ui/src/config/section/storage.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 03a78282b321..27f2a9bb0ced 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -311,12 +311,10 @@ export default { permission: ['listSnapshots'], resourceType: 'Snapshot', columns: () => { - var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize'] + var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize', 'created'] if (store.getters.features.snapshotshowchainsize) { - fields.push('chainsize') - fields.push('parentname') + fields.splice(fields.indexOf('created'), 0, 'chainsize', 'parentname') } - fields.push('created') if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('account') if (store.getters.listAllProjects) { @@ -329,7 +327,13 @@ export default { fields.push('zonename') return fields }, - details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'chainsize', 'parentname', 'account', 'domain', 'created'], + details: () => { + var fields = ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'account', 'domain', 'created'] + if (store.getters.features.snapshotshowchainsize) { + fields.splice(fields.indexOf('account'), 0, 'chainsize', 'parentname') + } + return fields + }, tabs: [ { name: 'details',