From d4bd5872313a795d4f97113dcc91a6cc732ebac3 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 15 Aug 2025 12:55:15 +0200 Subject: [PATCH 1/3] UI: fix addHost in zone wizard (#11401) --- ui/src/views/infra/zone/ZoneWizardLaunchZone.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue index 2467c52be38..83f116c2051 100644 --- a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue +++ b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue @@ -2082,7 +2082,11 @@ export default { resolve(result) }).catch(error => { message = error.response.headers['x-description'] - reject(message) + if (message.includes('is already in the database')) { + resolve() + } else { + reject(message) + } }) }) }, @@ -2094,11 +2098,7 @@ export default { resolve() }).catch(error => { message = error.response.headers['x-description'] - if (message.includes('is already in the database')) { - resolve() - } else { - reject(message) - } + reject(message) }) }) }, From 25f93b1d6b83acbdd14ba942c2f4086e9fdfd888 Mon Sep 17 00:00:00 2001 From: Rene Peinthor Date: Fri, 15 Aug 2025 15:50:39 +0200 Subject: [PATCH 2/3] linstor: fix getVolumeStats if multiple Linstor primary storages are used (#11397) We didn't account for caching the volume stats for each used Linstor cluster, so the first asked Linstor cluster would prevent caching for all the others and so null was returned. Now we have invalidate counters for each Linstor cluster and also store the cache result with the Linstor cluster address prefixed. --- plugins/storage/volume/linstor/CHANGELOG.md | 6 ++++ .../LinstorPrimaryDataStoreDriverImpl.java | 30 +++++++++++-------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index a96b7c75b2b..c0991a9aa2b 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to Linstor CloudStack plugin will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2025-08-05] + +### Fixed + +- getVolumeStats wasn't correctly working if multiple Linstor clusters/primary storages are used. + ## [2025-07-01] ### Fixed diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index 71e1b528506..0eefe0bcf9b 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -135,7 +135,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver private HostDao _hostDao; @Inject private VMTemplateDao _vmTemplateDao; - private long volumeStatsLastUpdate = 0L; + private final Map volumeStatsLastUpdate = new HashMap<>(); private final Map> volumeStats = new HashMap<>(); public LinstorPrimaryDataStoreDriverImpl() @@ -1533,11 +1533,12 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver /** * Updates the cache map containing current allocated size data. - * @param api Linstor Developers api object + * @param linstorAddr Linstor cluster api address */ - private void fillVolumeStatsCache(DevelopersApi api) { + private void fillVolumeStatsCache(String linstorAddr) { + final DevelopersApi api = LinstorUtil.getLinstorAPI(linstorAddr); try { - s_logger.trace("Start volume stats cache update"); + s_logger.trace("Start volume stats cache update for " + linstorAddr); List resources = api.viewResources( Collections.emptyList(), Collections.emptyList(), @@ -1564,14 +1565,14 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver } } - volumeStats.clear(); + volumeStats.keySet().removeIf(key -> key.startsWith(linstorAddr)); for (Map.Entry entry : allocSizeMap.entrySet()) { Long reserved = resSizeMap.getOrDefault(entry.getKey(), 0L); Pair volStat = new Pair<>(entry.getValue(), reserved); - volumeStats.put(entry.getKey(), volStat); + volumeStats.put(linstorAddr + "/" + entry.getKey(), volStat); } - volumeStatsLastUpdate = System.currentTimeMillis(); - s_logger.trace("Done volume stats cache update: " + volumeStats.size()); + volumeStatsLastUpdate.put(linstorAddr, System.currentTimeMillis()); + s_logger.debug(String.format("Done volume stats cache update for %s: %d", linstorAddr, volumeStats.size())); } catch (ApiException e) { s_logger.error("Unable to fetch Linstor resources: " + e.getBestMessage()); } @@ -1579,14 +1580,19 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver @Override public Pair getVolumeStats(StoragePool storagePool, String volumeId) { - final DevelopersApi api = LinstorUtil.getLinstorAPI(storagePool.getHostAddress()); + String linstorAddr = storagePool.getHostAddress(); synchronized (volumeStats) { - long invalidateCacheTime = volumeStatsLastUpdate + + long invalidateCacheTime = volumeStatsLastUpdate.getOrDefault(storagePool.getHostAddress(), 0L) + LinstorConfigurationManager.VolumeStatsCacheTime.value() * 1000; if (invalidateCacheTime < System.currentTimeMillis()) { - fillVolumeStatsCache(api); + fillVolumeStatsCache(storagePool.getHostAddress()); } - return volumeStats.get(LinstorUtil.RSC_PREFIX + volumeId); + String volumeKey = linstorAddr + "/" + LinstorUtil.RSC_PREFIX + volumeId; + Pair sizePair = volumeStats.get(volumeKey); + if (sizePair == null) { + s_logger.warn(String.format("Volumestats for %s not found in cache", volumeKey)); + } + return sizePair; } } From c6daeb4f78d5cd7c8865657aa49cce9bee6609aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Jandre?= <48719461+JoaoJandre@users.noreply.github.com> Date: Fri, 15 Aug 2025 11:22:50 -0300 Subject: [PATCH 3/3] fix snapshot physical size for primary storage (#11448) --- .../cloudstack/storage/snapshot/SnapshotObject.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index a3964bd461e..7602a142f88 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -172,10 +172,15 @@ public class SnapshotObject implements SnapshotInfo { @Override public long getPhysicalSize() { long physicalSize = 0; - SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Image, store.getId(), snapshot.getId()); - if (snapshotStore != null) { - physicalSize = snapshotStore.getPhysicalSize(); + for (DataStoreRole role : List.of(DataStoreRole.Image, DataStoreRole.Primary)) { + logger.trace("Retrieving snapshot [{}] size from {} storage.", snapshot.getUuid(), role); + SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(role, store.getId(), snapshot.getId()); + if (snapshotStore != null) { + return snapshotStore.getPhysicalSize(); + } + logger.trace("Snapshot [{}] size not found on {} storage.", snapshot.getUuid(), role); } + logger.warn("Snapshot [{}] reference not found in any storage. There may be an inconsistency on the database.", snapshot.getUuid()); return physicalSize; }