From 7b85ea1fb3322ac1900d5b2089d58fd007b3031d Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Tue, 29 Jul 2025 07:03:58 +0530 Subject: [PATCH 1/3] Show chain size in snapshot response for incremental snapshots --- .../apache/cloudstack/api/ApiConstants.java | 1 + .../api/response/SnapshotResponse.java | 8 ++++++++ .../api/query/dao/SnapshotJoinDaoImpl.java | 19 +++++++++++++++++++ .../storage/snapshot/SnapshotManager.java | 4 ++++ .../storage/snapshot/SnapshotManagerImpl.java | 2 +- tools/marvin/setup.py | 2 +- ui/public/locales/en.json | 1 + ui/src/components/view/DetailsTab.vue | 5 +++++ ui/src/config/section/storage.js | 2 +- 9 files changed, 41 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 00382b76f321..fa2f03e1e92d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1113,6 +1113,7 @@ public class ApiConstants { public static final String NETWORK_SPANNED_ZONES = "zonesnetworkspans"; public static final String METADATA = "metadata"; public static final String PHYSICAL_SIZE = "physicalsize"; + public static final String CHAIN_SIZE = "chainsize"; public static final String OVM3_POOL = "ovm3pool"; public static final String OVM3_CLUSTER = "ovm3cluster"; public static final String OVM3_VIP = "ovm3vip"; 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 9f7a7f42dec9..5d6756c950d6 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 @@ -107,6 +107,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements @Param(description = "physical size of backedup snapshot on image store") private long physicalSize; + @SerializedName(ApiConstants.CHAIN_SIZE) + @Param(description = "chain size of snapshot including all parent snapshots. Shown only for incremental snapshots if snapshot.show.chain.size setting is set to true", since = "4.21.0") + private Long chainSize; + @SerializedName(ApiConstants.ZONE_ID) @Param(description = "id of the availability zone") private String zoneId; @@ -244,6 +248,10 @@ public void setPhysicalSize(long physicalSize) { this.physicalSize = physicalSize; } + public void setChainSize(long chainSize) { + this.chainSize = chainSize; + } + @Override public void setProjectId(String projectId) { this.projectId = projectId; 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 2944e69c22a7..bca60383501f 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 @@ -46,6 +46,7 @@ import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; +import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.utils.db.Filter; @@ -96,9 +97,27 @@ private void setSnapshotInfoDetailsInResponse(SnapshotJoinVO snapshot, SnapshotR } else { snapshotResponse.setRevertable(snapshotInfo.isRevertable()); snapshotResponse.setPhysicalSize(snapshotInfo.getPhysicalSize()); + + boolean showChainSize = SnapshotManager.snapshotShowChainSize.valueIn(snapshot.getDataCenterId()); + if (showChainSize && snapshotInfo.getParent() != null) { + long chainSize = calculateChainSize(snapshotInfo); + snapshotResponse.setChainSize(chainSize); + } } } + private long calculateChainSize(SnapshotInfo snapshotInfo) { + long chainSize = snapshotInfo.getPhysicalSize(); + SnapshotInfo parent = snapshotInfo.getParent(); + + while (parent != null) { + chainSize += parent.getPhysicalSize(); + parent = parent.getParent(); + } + + return chainSize; + } + private String getSnapshotStatus(SnapshotJoinVO snapshot) { String status = snapshot.getStatus().toString(); if (snapshot.getDownloadState() == null) { diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java index aea5961d0472..048a1833310e 100644 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java @@ -61,6 +61,10 @@ public interface SnapshotManager extends Configurable { ConfigKey snapshotDeltaMax = new ConfigKey<>(Integer.class, "snapshot.delta.max", "Snapshots", "16", "Max delta snapshots between two full snapshots. " + "Only valid for KVM and XenServer.", true, ConfigKey.Scope.Global, null); + ConfigKey snapshotShowChainSize = new ConfigKey<>(Boolean.class, "snapshot.show.chain.size", "Snapshots", "true", + "Whether to show chain size (sum of physical size of snapshot and all its parents) for incremental snapshots in the snapshot response", + true, ConfigKey.Scope.Global, null); + void deletePoliciesForVolume(Long volumeId); /** diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 1e54fc6e224e..5a4d6a68402a 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -294,7 +294,7 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { return new ConfigKey[] {BackupRetryAttempts, BackupRetryInterval, SnapshotHourlyMax, SnapshotDailyMax, SnapshotMonthlyMax, SnapshotWeeklyMax, usageSnapshotSelection, - SnapshotInfo.BackupSnapshotAfterTakingSnapshot, VmStorageSnapshotKvm, kvmIncrementalSnapshot, snapshotDeltaMax}; + SnapshotInfo.BackupSnapshotAfterTakingSnapshot, VmStorageSnapshotKvm, kvmIncrementalSnapshot, snapshotDeltaMax, snapshotShowChainSize}; } @Override diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py index 11a63a96aced..803829736892 100644 --- a/tools/marvin/setup.py +++ b/tools/marvin/setup.py @@ -27,7 +27,7 @@ raise RuntimeError("python setuptools is required to build Marvin") -VERSION = "4.21.0.0-SNAPSHOT" +VERSION = "4.21.0.0" setup(name="Marvin", version=VERSION, diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 4ec03d375a37..2037a13ef222 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -505,6 +505,7 @@ "label.certificate.upload.failed": "Certificate upload failed", "label.certificate.upload.failed.description": "Failed to update SSL Certificate. Failed to pass certificate validation check.", "label.certificateid": "Certificate ID", +"label.chainsize": "Chain size", "label.change": "Change", "label.change.affinity": "Change affinity", "label.change.bgp.peers": "Change BGP peers", diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index 3a4d0fa043b6..aad29b265452 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -87,6 +87,11 @@ {{ parseFloat(dataResource.virtualsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB +
+
+ {{ parseFloat(dataResource.chainsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB +
+
{{ $t(dataResource[item].toLowerCase()) }} {{ dataResource[item] }} diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index bcd48944497b..df64b0e6a99f 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -324,7 +324,7 @@ export default { fields.push('zonename') return fields }, - details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'account', 'domain', 'created'], + details: ['name', 'id', 'volumename', 'volumetype', 'snapshottype', 'intervaltype', 'physicalsize', 'virtualsize', 'chainsize', 'account', 'domain', 'created'], tabs: [ { name: 'details', From 4e2d52885dcfba00a6564ad7979f306f0529be74 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:00:29 +0530 Subject: [PATCH 2/3] set default setting as false. revert setup.py. --- .../main/java/com/cloud/storage/snapshot/SnapshotManager.java | 2 +- tools/marvin/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java index 048a1833310e..b8ae367bbabd 100644 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java @@ -61,7 +61,7 @@ public interface SnapshotManager extends Configurable { ConfigKey snapshotDeltaMax = new ConfigKey<>(Integer.class, "snapshot.delta.max", "Snapshots", "16", "Max delta snapshots between two full snapshots. " + "Only valid for KVM and XenServer.", true, ConfigKey.Scope.Global, null); - ConfigKey snapshotShowChainSize = new ConfigKey<>(Boolean.class, "snapshot.show.chain.size", "Snapshots", "true", + ConfigKey snapshotShowChainSize = new ConfigKey<>(Boolean.class, "snapshot.show.chain.size", "Snapshots", "false", "Whether to show chain size (sum of physical size of snapshot and all its parents) for incremental snapshots in the snapshot response", true, ConfigKey.Scope.Global, null); diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py index 803829736892..11a63a96aced 100644 --- a/tools/marvin/setup.py +++ b/tools/marvin/setup.py @@ -27,7 +27,7 @@ raise RuntimeError("python setuptools is required to build Marvin") -VERSION = "4.21.0.0" +VERSION = "4.21.0.0-SNAPSHOT" setup(name="Marvin", version=VERSION, From 9edcf3730a48299d0b305d28e2213ab737270d7f Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:05:31 +0530 Subject: [PATCH 3/3] DetailsTab.vue - extract common method sizeInGiB --- ui/src/components/view/DetailsTab.vue | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index aad29b265452..c4cd57b97ec1 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -74,22 +74,22 @@
- {{ parseFloat(dataResource.size / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + {{ sizeInGiB(dataResource.size) }} GiB
- {{ parseFloat(dataResource.physicalsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + {{ sizeInGiB(dataResource.physicalsize) }} GiB
- {{ parseFloat(dataResource.virtualsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + {{ sizeInGiB(dataResource.virtualsize) }} GiB
- {{ parseFloat(dataResource.chainsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GiB + {{ sizeInGiB(dataResource.chainsize) }} GiB
@@ -477,6 +477,12 @@ export default { } return `label.${source}` + }, + sizeInGiB (sizeInBytes) { + if (!sizeInBytes || sizeInBytes === 0) { + return '0.00' + } + return parseFloat(sizeInBytes / (1024.0 * 1024.0 * 1024.0)).toFixed(2) } } }