Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/storage/volume/linstor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ 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-05-07]

### Added
- Implemented storage/volume stats

## [2025-03-13]

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
import com.linbit.linstor.api.model.ResourceDefinitionCloneRequest;
import com.linbit.linstor.api.model.ResourceDefinitionCloneStarted;
import com.linbit.linstor.api.model.ResourceDefinitionCreate;

import com.linbit.linstor.api.model.ResourceDefinitionModify;
import com.linbit.linstor.api.model.ResourceGroup;
import com.linbit.linstor.api.model.ResourceGroupSpawn;
import com.linbit.linstor.api.model.ResourceMakeAvailable;
import com.linbit.linstor.api.model.ResourceWithVolumes;
import com.linbit.linstor.api.model.Snapshot;
import com.linbit.linstor.api.model.SnapshotRestore;
import com.linbit.linstor.api.model.VolumeDefinition;
Expand Down Expand Up @@ -132,6 +132,9 @@
@Inject
private HostDao _hostDao;

private long volumeStatsLastUpdate = 0L;
private final Map<String, Pair<Long, Long>> volumeStats = new HashMap<>();

public LinstorPrimaryDataStoreDriverImpl()
{
}
Expand Down Expand Up @@ -401,9 +404,9 @@
}
}

private String getRscGrp(StoragePoolVO storagePoolVO) {
return storagePoolVO.getUserInfo() != null && !storagePoolVO.getUserInfo().isEmpty() ?
storagePoolVO.getUserInfo() : "DfltRscGrp";
private String getRscGrp(StoragePool storagePool) {

Check warning on line 407 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L407 was not covered by tests
return storagePool.getUserInfo() != null && !storagePool.getUserInfo().isEmpty() ?
storagePool.getUserInfo() : "DfltRscGrp";

Check warning on line 409 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L409 was not covered by tests
}

/**
Expand Down Expand Up @@ -616,7 +619,7 @@
*/
private void updateRscGrpIfNecessary(DevelopersApi api, String rscName, String tgtRscGrp) throws ApiException {
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(
Collections.singletonList(rscName), null, null, null);
Collections.singletonList(rscName), false, null, null, null);

Check warning on line 622 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L622 was not covered by tests
if (rscDfns != null && !rscDfns.isEmpty()) {
ResourceDefinition rscDfn = rscDfns.get(0);

Expand Down Expand Up @@ -646,7 +649,7 @@
private void deleteTemplateForProps(
DevelopersApi api, String rscName) throws ApiException {
List<ResourceDefinition> rdList = api.resourceDefinitionList(
Collections.singletonList(rscName), null, null, null);
Collections.singletonList(rscName), false, null, null, null);

Check warning on line 652 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L652 was not covered by tests

if (CollectionUtils.isNotEmpty(rdList)) {
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
Expand Down Expand Up @@ -1504,22 +1507,77 @@

@Override
public boolean canProvideStorageStats() {
return false;
return true;

Check warning on line 1510 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1510 was not covered by tests
}

@Override
public Pair<Long, Long> getStorageStats(StoragePool storagePool) {
return null;
s_logger.debug(String.format("Requesting storage stats: %s", storagePool));
return LinstorUtil.getStorageStats(storagePool.getHostAddress(), getRscGrp(storagePool));

Check warning on line 1516 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java#L1515-L1516

Added lines #L1515 - L1516 were not covered by tests
}

@Override
public boolean canProvideVolumeStats() {
return false;
return LinstorConfigurationManager.VolumeStatsCacheTime.value() > 0;
}

Check warning on line 1522 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1522 was not covered by tests

/**
* Updates the cache map containing current allocated size data.
* @param api Linstor Developers api object
*/
private void fillVolumeStatsCache(DevelopersApi api) {
try {
s_logger.trace("Start volume stats cache update");
List<ResourceWithVolumes> resources = api.viewResources(
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),

Check warning on line 1534 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java#L1528-L1534

Added lines #L1528 - L1534 were not covered by tests
null,
null,
null);

List<ResourceDefinition> rscDfns = api.resourceDefinitionList(
Collections.emptyList(), true, null, null, null);

Check warning on line 1540 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java#L1539-L1540

Added lines #L1539 - L1540 were not covered by tests

HashMap<String, Long> resSizeMap = new HashMap<>();

Check warning on line 1542 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1542 was not covered by tests
for (ResourceDefinition rscDfn : rscDfns) {
if (CollectionUtils.isNotEmpty(rscDfn.getVolumeDefinitions())) {
resSizeMap.put(rscDfn.getName(), rscDfn.getVolumeDefinitions().get(0).getSizeKib() * 1024);

Check warning on line 1545 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1545 was not covered by tests
}
}

Check warning on line 1547 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1547 was not covered by tests

HashMap<String, Long> allocSizeMap = new HashMap<>();

Check warning on line 1549 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1549 was not covered by tests
for (ResourceWithVolumes rsc : resources) {
if (!LinstorUtil.isRscDiskless(rsc) && !rsc.getVolumes().isEmpty()) {
long allocatedBytes = allocSizeMap.getOrDefault(rsc.getName(), 0L);
allocSizeMap.put(rsc.getName(), Math.max(allocatedBytes, rsc.getVolumes().get(0).getAllocatedSizeKib() * 1024));

Check warning on line 1553 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java#L1552-L1553

Added lines #L1552 - L1553 were not covered by tests
}
}

Check warning on line 1555 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1555 was not covered by tests

volumeStats.clear();

Check warning on line 1557 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1557 was not covered by tests
for (Map.Entry<String, Long> entry : allocSizeMap.entrySet()) {
Long reserved = resSizeMap.getOrDefault(entry.getKey(), 0L);
Pair<Long, Long> volStat = new Pair<>(entry.getValue(), reserved);
volumeStats.put(entry.getKey(), volStat);
}
volumeStatsLastUpdate = System.currentTimeMillis();
s_logger.trace("Done volume stats cache update: " + volumeStats.size());
} catch (ApiException e) {
s_logger.error("Unable to fetch Linstor resources: " + e.getBestMessage());
}

Check warning on line 1567 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java#L1559-L1567

Added lines #L1559 - L1567 were not covered by tests
}

@Override
public Pair<Long, Long> getVolumeStats(StoragePool storagePool, String volumeId) {
return null;
final DevelopersApi api = LinstorUtil.getLinstorAPI(storagePool.getHostAddress());
synchronized (volumeStats) {
long invalidateCacheTime = volumeStatsLastUpdate +
LinstorConfigurationManager.VolumeStatsCacheTime.value() * 1000;

Check warning on line 1575 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java#L1572-L1575

Added lines #L1572 - L1575 were not covered by tests
if (invalidateCacheTime < System.currentTimeMillis()) {
fillVolumeStatsCache(api);

Check warning on line 1577 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1577 was not covered by tests
}
return volumeStats.get(LinstorUtil.RSC_PREFIX + volumeId);

Check warning on line 1579 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L1579 was not covered by tests
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@
public static final ConfigKey<Boolean> BackupSnapshots = new ConfigKey<>(Boolean.class, "lin.backup.snapshots", "Advanced", "true",
"Backup Linstor primary storage snapshots to secondary storage (deleting ps snapshot)", true, ConfigKey.Scope.Global, null);

public static final ConfigKey<?>[] CONFIG_KEYS = new ConfigKey<?>[] { BackupSnapshots };
public static final ConfigKey<Integer> VolumeStatsCacheTime = new ConfigKey<>("Advanced", Integer.class,

Check warning on line 27 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorConfigurationManager.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L27 was not covered by tests
"lin.volumes.stats.cachetime", "300",
"Cache time of volume stats for Linstor volumes. 0 to disable volume stats",
false);

public static final ConfigKey<?>[] CONFIG_KEYS = new ConfigKey<?>[] {

Check warning on line 32 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorConfigurationManager.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L32 was not covered by tests
BackupSnapshots, VolumeStatsCacheTime
};

@Override
public String getConfigComponentName()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,30 @@
}
}

public static Pair<Long, Long> getStorageStats(String linstorUrl, String rscGroupName) {
DevelopersApi linstorApi = getLinstorAPI(linstorUrl);
try {
List<StoragePool> storagePools = LinstorUtil.getRscGroupStoragePools(linstorApi, rscGroupName);

Check warning on line 201 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java#L198-L201

Added lines #L198 - L201 were not covered by tests

long capacity = storagePools.stream()

Check warning on line 203 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L203 was not covered by tests
.filter(sp -> sp.getProviderKind() != ProviderKind.DISKLESS)
.mapToLong(sp -> sp.getTotalCapacity() != null ? sp.getTotalCapacity() : 0L)
.sum() * 1024; // linstor uses kiB

Check warning on line 206 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L206 was not covered by tests

long used = storagePools.stream()

Check warning on line 208 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L208 was not covered by tests
.filter(sp -> sp.getProviderKind() != ProviderKind.DISKLESS)
.mapToLong(sp -> sp.getTotalCapacity() != null && sp.getFreeCapacity() != null ?
sp.getTotalCapacity() - sp.getFreeCapacity() : 0L)
.sum() * 1024; // linstor uses Kib
s_logger.debug(
String.format("Linstor(%s;%s): storageStats -> %d/%d", linstorUrl, rscGroupName, capacity, used));
return new Pair<>(capacity, used);
} catch (ApiException apiEx) {
s_logger.error(apiEx.getMessage());
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);

Check warning on line 218 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java#L211-L218

Added lines #L211 - L218 were not covered by tests
}
}

Check warning on line 220 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L220 was not covered by tests

/**
* Check if any resource of the given name is InUse on any host.
*
Expand Down Expand Up @@ -303,7 +327,7 @@
public static List<ResourceDefinition> getRDListStartingWith(DevelopersApi api, String startWith)
throws ApiException
{
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, null, null, null);
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, false, null, null, null);

Check warning on line 330 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L330 was not covered by tests

return rscDfns.stream()
.filter(rscDfn -> rscDfn.getName().toLowerCase().startsWith(startWith.toLowerCase()))
Expand Down Expand Up @@ -386,7 +410,7 @@
*/
public static ResourceDefinition findResourceDefinition(DevelopersApi api, String rscName, String rscGrpName)
throws ApiException {
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, null, null, null);
List<ResourceDefinition> rscDfns = api.resourceDefinitionList(null, false, null, null, null);

Check warning on line 413 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L413 was not covered by tests

List<ResourceDefinition> rdsStartingWith = rscDfns.stream()
.filter(rscDfn -> rscDfn.getName().toLowerCase().startsWith(rscName.toLowerCase()))
Expand All @@ -402,4 +426,8 @@

return rd.orElseGet(() -> rdsStartingWith.get(0));
}

public static boolean isRscDiskless(ResourceWithVolumes rsc) {

Check warning on line 430 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L430 was not covered by tests
return rsc.getFlags() != null && rsc.getFlags().contains(ApiConsts.FLAG_DISKLESS);
}

Check warning on line 432 in plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L432 was not covered by tests
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@
<cs.nitro.version>10.1</cs.nitro.version>
<cs.opensaml.version>2.6.6</cs.opensaml.version>
<cs.rados-java.version>0.6.0</cs.rados-java.version>
<cs.java-linstor.version>0.6.0</cs.java-linstor.version>
<cs.java-linstor.version>0.6.1</cs.java-linstor.version>
<cs.reflections.version>0.10.2</cs.reflections.version>
<cs.servicemix.version>3.4.4_1</cs.servicemix.version>
<cs.servlet.version>4.0.1</cs.servlet.version>
Expand Down