Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.cloudstack.storage.datastore.adapter.ProviderAdapter;
Expand Down Expand Up @@ -452,18 +453,49 @@ public void disconnect() {
@Override
public ProviderVolumeStorageStats getManagedStorageStats() {
FlashArrayPod pod = getVolumeNamespace(this.pod);
// just in case
if (pod == null || pod.getFootprint() == 0) {
if (pod == null) {
return null;
}
Long capacityBytes = pod.getQuotaLimit();
Long usedBytes = pod.getQuotaLimit() - (pod.getQuotaLimit() - pod.getFootprint());
if (capacityBytes == null || capacityBytes == 0) {
// Pod has no explicit quota set; report the array total physical
// capacity so the CloudStack allocator has a real ceiling to plan
// against rather than bailing out with a zero-capacity pool.
capacityBytes = getArrayTotalCapacity();
}
Comment on lines +460 to +465
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling GET("/arrays?space=true") inside getManagedStorageStats() can add a second REST request on every storage-stats refresh whenever a pod has no quota. Since FlashArrayAdapterFactory constructs a new adapter per call, this likely won’t be amortized/cached and could create avoidable API load. Consider memoizing the array capacity (e.g., static cache keyed by URL with a TTL) or persisting the discovered capacity into CloudStack pool details so subsequent stats calls don’t need to re-query /arrays each time.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@winterhazel winterhazel May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@genegr I do not think that this cache suggested by Copilot is needed, but its up to you. getManagedStorageStats is called only every 60 seconds by default.

Copy link
Copy Markdown
Contributor Author

@genegr genegr May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Pushed 6f64daef2b removing the cache. You're right that with getManagedStorageStats() polled every 60s, a single extra GET /arrays?space=true per poll (and only when a pod has no quota) is negligible, and the static shared-state cache wasn't worth the complexity. getArrayTotalCapacity() now does a direct lookup; dropped the CachedCapacity helper and the two java.util.concurrent imports it needed.

if (capacityBytes == null || capacityBytes == 0) {
return null;
}
Long usedBytes = pod.getFootprint();
if (usedBytes == null) {
usedBytes = 0L;
}
ProviderVolumeStorageStats stats = new ProviderVolumeStorageStats();
stats.setCapacityInBytes(capacityBytes);
stats.setActualUsedInBytes(usedBytes);
return stats;
}

private Long getArrayTotalCapacity() {
try {
FlashArrayList<Map<String, Object>> list = GET("/arrays?space=true",
new TypeReference<FlashArrayList<Map<String, Object>>>() {
});
if (list != null && CollectionUtils.isNotEmpty(list.getItems())) {
Object cap = list.getItems().get(0).get("capacity");
if (cap instanceof Number) {
return ((Number) cap).longValue();
}
}
} catch (Exception e) {
logger.warn("Could not retrieve total capacity for FlashArray [{}] (pod [{}]): {}",
this.url, this.pod, e.getMessage());
logger.debug("Stack trace for array total capacity lookup failure on FlashArray [{}] (pod [{}])",
this.url, this.pod, e);
}
return null;
}

@Override
public ProviderVolumeStats getVolumeStats(ProviderAdapterContext context, ProviderAdapterDataObject dataObject) {
ProviderVolume vol = getVolume(dataObject.getExternalName());
Expand Down