Skip to content

Commit dd84c74

Browse files
author
Daan Hoogland
committed
Merge branch '4.19' into 4.20
2 parents 011fced + 88ce639 commit dd84c74

File tree

8 files changed

+370
-244
lines changed

8 files changed

+370
-244
lines changed

plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java

Lines changed: 200 additions & 186 deletions
Large diffs are not rendered by default.

plugins/storage/volume/linstor/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to Linstor CloudStack plugin will be documented in this file
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2025-05-07]
9+
10+
### Added
11+
- Implemented storage/volume stats
12+
813
## [2025-03-13]
914

1015
### Fixed

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
import com.linbit.linstor.api.model.ResourceDefinitionCloneRequest;
2929
import com.linbit.linstor.api.model.ResourceDefinitionCloneStarted;
3030
import com.linbit.linstor.api.model.ResourceDefinitionCreate;
31-
3231
import com.linbit.linstor.api.model.ResourceDefinitionModify;
3332
import com.linbit.linstor.api.model.ResourceGroup;
3433
import com.linbit.linstor.api.model.ResourceGroupSpawn;
3534
import com.linbit.linstor.api.model.ResourceMakeAvailable;
35+
import com.linbit.linstor.api.model.ResourceWithVolumes;
3636
import com.linbit.linstor.api.model.Snapshot;
3737
import com.linbit.linstor.api.model.SnapshotRestore;
3838
import com.linbit.linstor.api.model.VolumeDefinition;
@@ -134,6 +134,9 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
134134
@Inject
135135
private HostDao _hostDao;
136136

137+
private long volumeStatsLastUpdate = 0L;
138+
private final Map<String, Pair<Long, Long>> volumeStats = new HashMap<>();
139+
137140
public LinstorPrimaryDataStoreDriverImpl()
138141
{
139142
}
@@ -403,9 +406,9 @@ private void applyQoSSettings(StoragePoolVO storagePool, DevelopersApi api, Stri
403406
}
404407
}
405408

406-
private String getRscGrp(StoragePoolVO storagePoolVO) {
407-
return storagePoolVO.getUserInfo() != null && !storagePoolVO.getUserInfo().isEmpty() ?
408-
storagePoolVO.getUserInfo() : "DfltRscGrp";
409+
private String getRscGrp(StoragePool storagePool) {
410+
return storagePool.getUserInfo() != null && !storagePool.getUserInfo().isEmpty() ?
411+
storagePool.getUserInfo() : "DfltRscGrp";
409412
}
410413

411414
/**
@@ -618,7 +621,7 @@ private void resizeResource(DevelopersApi api, String resourceName, long sizeByt
618621
*/
619622
private void updateRscGrpIfNecessary(DevelopersApi api, String rscName, String tgtRscGrp) throws ApiException {
620623
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(
621-
Collections.singletonList(rscName), null, null, null);
624+
Collections.singletonList(rscName), false, null, null, null);
622625
if (rscDfns != null && !rscDfns.isEmpty()) {
623626
ResourceDefinition rscDfn = rscDfns.get(0);
624627

@@ -648,7 +651,7 @@ private void updateRscGrpIfNecessary(DevelopersApi api, String rscName, String t
648651
private void deleteTemplateForProps(
649652
DevelopersApi api, String rscName) throws ApiException {
650653
List<ResourceDefinition> rdList = api.resourceDefinitionList(
651-
Collections.singletonList(rscName), null, null, null);
654+
Collections.singletonList(rscName), false, null, null, null);
652655

653656
if (CollectionUtils.isNotEmpty(rdList)) {
654657
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
@@ -1506,22 +1509,77 @@ public void takeSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback<Crea
15061509

15071510
@Override
15081511
public boolean canProvideStorageStats() {
1509-
return false;
1512+
return true;
15101513
}
15111514

15121515
@Override
15131516
public Pair<Long, Long> getStorageStats(StoragePool storagePool) {
1514-
return null;
1517+
logger.debug(String.format("Requesting storage stats: %s", storagePool));
1518+
return LinstorUtil.getStorageStats(storagePool.getHostAddress(), getRscGrp(storagePool));
15151519
}
15161520

15171521
@Override
15181522
public boolean canProvideVolumeStats() {
1519-
return false;
1523+
return LinstorConfigurationManager.VolumeStatsCacheTime.value() > 0;
1524+
}
1525+
1526+
/**
1527+
* Updates the cache map containing current allocated size data.
1528+
* @param api Linstor Developers api object
1529+
*/
1530+
private void fillVolumeStatsCache(DevelopersApi api) {
1531+
try {
1532+
logger.trace("Start volume stats cache update");
1533+
List<ResourceWithVolumes> resources = api.viewResources(
1534+
Collections.emptyList(),
1535+
Collections.emptyList(),
1536+
Collections.emptyList(),
1537+
null,
1538+
null,
1539+
null);
1540+
1541+
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(
1542+
Collections.emptyList(), true, null, null, null);
1543+
1544+
HashMap<String, Long> resSizeMap = new HashMap<>();
1545+
for (ResourceDefinition rscDfn : rscDfns) {
1546+
if (CollectionUtils.isNotEmpty(rscDfn.getVolumeDefinitions())) {
1547+
resSizeMap.put(rscDfn.getName(), rscDfn.getVolumeDefinitions().get(0).getSizeKib() * 1024);
1548+
}
1549+
}
1550+
1551+
HashMap<String, Long> allocSizeMap = new HashMap<>();
1552+
for (ResourceWithVolumes rsc : resources) {
1553+
if (!LinstorUtil.isRscDiskless(rsc) && !rsc.getVolumes().isEmpty()) {
1554+
long allocatedBytes = allocSizeMap.getOrDefault(rsc.getName(), 0L);
1555+
allocSizeMap.put(rsc.getName(), Math.max(allocatedBytes, rsc.getVolumes().get(0).getAllocatedSizeKib() * 1024));
1556+
}
1557+
}
1558+
1559+
volumeStats.clear();
1560+
for (Map.Entry<String, Long> entry : allocSizeMap.entrySet()) {
1561+
Long reserved = resSizeMap.getOrDefault(entry.getKey(), 0L);
1562+
Pair<Long, Long> volStat = new Pair<>(entry.getValue(), reserved);
1563+
volumeStats.put(entry.getKey(), volStat);
1564+
}
1565+
volumeStatsLastUpdate = System.currentTimeMillis();
1566+
logger.trace("Done volume stats cache update: {}", volumeStats.size());
1567+
} catch (ApiException e) {
1568+
logger.error("Unable to fetch Linstor resources: {}", e.getBestMessage());
1569+
}
15201570
}
15211571

15221572
@Override
15231573
public Pair<Long, Long> getVolumeStats(StoragePool storagePool, String volumeId) {
1524-
return null;
1574+
final DevelopersApi api = LinstorUtil.getLinstorAPI(storagePool.getHostAddress());
1575+
synchronized (volumeStats) {
1576+
long invalidateCacheTime = volumeStatsLastUpdate +
1577+
LinstorConfigurationManager.VolumeStatsCacheTime.value() * 1000;
1578+
if (invalidateCacheTime < System.currentTimeMillis()) {
1579+
fillVolumeStatsCache(api);
1580+
}
1581+
return volumeStats.get(LinstorUtil.RSC_PREFIX + volumeId);
1582+
}
15251583
}
15261584

15271585
@Override

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorConfigurationManager.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ public class LinstorConfigurationManager implements Configurable
2424
public static final ConfigKey<Boolean> BackupSnapshots = new ConfigKey<>(Boolean.class, "lin.backup.snapshots", "Advanced", "true",
2525
"Backup Linstor primary storage snapshots to secondary storage (deleting ps snapshot)", true, ConfigKey.Scope.Global, null);
2626

27-
public static final ConfigKey<?>[] CONFIG_KEYS = new ConfigKey<?>[] { BackupSnapshots };
27+
public static final ConfigKey<Integer> VolumeStatsCacheTime = new ConfigKey<>("Advanced", Integer.class,
28+
"lin.volumes.stats.cachetime", "300",
29+
"Cache time of volume stats for Linstor volumes. 0 to disable volume stats",
30+
false);
31+
32+
public static final ConfigKey<?>[] CONFIG_KEYS = new ConfigKey<?>[] {
33+
BackupSnapshots, VolumeStatsCacheTime
34+
};
2835

2936
@Override
3037
public String getConfigComponentName()

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,30 @@ public static long getCapacityBytes(String linstorUrl, String rscGroupName) {
196196
}
197197
}
198198

199+
public static Pair<Long, Long> getStorageStats(String linstorUrl, String rscGroupName) {
200+
DevelopersApi linstorApi = getLinstorAPI(linstorUrl);
201+
try {
202+
List<StoragePool> storagePools = LinstorUtil.getRscGroupStoragePools(linstorApi, rscGroupName);
203+
204+
long capacity = storagePools.stream()
205+
.filter(sp -> sp.getProviderKind() != ProviderKind.DISKLESS)
206+
.mapToLong(sp -> sp.getTotalCapacity() != null ? sp.getTotalCapacity() : 0L)
207+
.sum() * 1024; // linstor uses kiB
208+
209+
long used = storagePools.stream()
210+
.filter(sp -> sp.getProviderKind() != ProviderKind.DISKLESS)
211+
.mapToLong(sp -> sp.getTotalCapacity() != null && sp.getFreeCapacity() != null ?
212+
sp.getTotalCapacity() - sp.getFreeCapacity() : 0L)
213+
.sum() * 1024; // linstor uses Kib
214+
LOGGER.debug(
215+
String.format("Linstor(%s;%s): storageStats -> %d/%d", linstorUrl, rscGroupName, capacity, used));
216+
return new Pair<>(capacity, used);
217+
} catch (ApiException apiEx) {
218+
LOGGER.error(apiEx.getMessage());
219+
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
220+
}
221+
}
222+
199223
/**
200224
* Check if any resource of the given name is InUse on any host.
201225
*
@@ -304,7 +328,7 @@ public static ApiCallRcList applyAuxProps(DevelopersApi api, String rscName, Str
304328
public static List<ResourceDefinition> getRDListStartingWith(DevelopersApi api, String startWith)
305329
throws ApiException
306330
{
307-
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, null, null, null);
331+
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, false, null, null, null);
308332

309333
return rscDfns.stream()
310334
.filter(rscDfn -> rscDfn.getName().toLowerCase().startsWith(startWith.toLowerCase()))
@@ -387,7 +411,7 @@ public static void setAuxTemplateForProperty(DevelopersApi api, String rscName,
387411
*/
388412
public static ResourceDefinition findResourceDefinition(DevelopersApi api, String rscName, String rscGrpName)
389413
throws ApiException {
390-
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, null, null, null);
414+
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, false, null, null, null);
391415

392416
List<ResourceDefinition> rdsStartingWith = rscDfns.stream()
393417
.filter(rscDfn -> rscDfn.getName().toLowerCase().startsWith(rscName.toLowerCase()))
@@ -403,4 +427,8 @@ public static ResourceDefinition findResourceDefinition(DevelopersApi api, Strin
403427

404428
return rd.orElseGet(() -> rdsStartingWith.get(0));
405429
}
430+
431+
public static boolean isRscDiskless(ResourceWithVolumes rsc) {
432+
return rsc.getFlags() != null && rsc.getFlags().contains(ApiConsts.FLAG_DISKLESS);
433+
}
406434
}

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
<cs.nitro.version>10.1</cs.nitro.version>
176176
<cs.opensaml.version>2.6.6</cs.opensaml.version>
177177
<cs.rados-java.version>0.6.0</cs.rados-java.version>
178-
<cs.java-linstor.version>0.6.0</cs.java-linstor.version>
178+
<cs.java-linstor.version>0.6.1</cs.java-linstor.version>
179179
<cs.reflections.version>0.10.2</cs.reflections.version>
180180
<cs.servicemix.version>3.4.4_1</cs.servicemix.version>
181181
<cs.servlet.version>4.0.1</cs.servlet.version>

0 commit comments

Comments
 (0)