This is an automated email from the ASF dual-hosted git repository.
nvazquez pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.22 by this push:
new 131ea9f7ace Fix PowerFlex 4.x issues with take & revert instance
snapshots (#12880)
131ea9f7ace is described below
commit 131ea9f7aceb4c12d8cbf0fe92bcc0419f40a2bc
Author: owsferraro <[email protected]>
AuthorDate: Fri Mar 27 11:22:08 2026 +0100
Fix PowerFlex 4.x issues with take & revert instance snapshots (#12880)
* fixed database update on snapshot with multiple volumes and an api change
* changed overwritevolumecontent based on powerflex version and removed
unnecessary comments
* Update
plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
Co-authored-by: Suresh Kumar Anaparti <[email protected]>
* Update
plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
Co-authored-by: Suresh Kumar Anaparti <[email protected]>
* Update
plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
Co-authored-by: Suresh Kumar Anaparti <[email protected]>
---------
Co-authored-by: Suresh Kumar Anaparti <[email protected]>
---
.../vmsnapshot/ScaleIOVMSnapshotStrategy.java | 33 ++++++++++--
.../datastore/client/ScaleIOGatewayClientImpl.java | 61 +++++++++++++++++++++-
2 files changed, 89 insertions(+), 5 deletions(-)
diff --git
a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
index 7199fce1d34..aced750bd32 100644
---
a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
+++
b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java
@@ -40,6 +40,7 @@ import
org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.commons.collections.CollectionUtils;
+import org.apache.cloudstack.storage.datastore.api.Volume;
import com.cloud.agent.api.VMSnapshotTO;
import com.cloud.alert.AlertManager;
@@ -200,11 +201,35 @@ public class ScaleIOVMSnapshotStrategy extends
ManagerBase implements VMSnapshot
if (volumeIds != null && !volumeIds.isEmpty()) {
List<VMSnapshotDetailsVO> vmSnapshotDetails = new
ArrayList<VMSnapshotDetailsVO>();
vmSnapshotDetails.add(new
VMSnapshotDetailsVO(vmSnapshot.getId(), "SnapshotGroupId", snapshotGroupId,
false));
+ Map<String, String> snapshotNameToSrcPathMap = new
HashMap<>();
+ for (Map.Entry<String, String> entry :
srcVolumeDestSnapshotMap.entrySet()) {
+ snapshotNameToSrcPathMap.put(entry.getValue(),
entry.getKey());
+ }
+
+ for (String snapshotVolumeId : volumeIds) {
+ // Use getVolume() to fetch snapshot volume details
and get its name
+ Volume snapshotVolume =
client.getVolume(snapshotVolumeId);
+ if (snapshotVolume == null) {
+ throw new CloudRuntimeException("Cannot find
snapshot volume with id: " + snapshotVolumeId);
+ }
+ String snapshotName = snapshotVolume.getName();
+
+ // Match back to source volume path
+ String srcVolumePath =
snapshotNameToSrcPathMap.get(snapshotName);
+ if (srcVolumePath == null) {
+ throw new CloudRuntimeException("Cannot match
snapshot " + snapshotName + " to a source volume");
+ }
+
+ // Find the matching VolumeObjectTO by path
+ VolumeObjectTO matchedVolume = volumeTOs.stream()
+ .filter(v ->
ScaleIOUtil.getVolumePath(v.getPath()).equals(srcVolumePath))
+ .findFirst()
+ .orElseThrow(() -> new
CloudRuntimeException("Cannot find source volume for path: " + srcVolumePath));
- for (int index = 0; index < volumeIds.size(); index++) {
- String volumeSnapshotName =
srcVolumeDestSnapshotMap.get(ScaleIOUtil.getVolumePath(volumeTOs.get(index).getPath()));
- String pathWithScaleIOVolumeName =
ScaleIOUtil.updatedPathWithVolumeName(volumeIds.get(index), volumeSnapshotName);
- vmSnapshotDetails.add(new
VMSnapshotDetailsVO(vmSnapshot.getId(), "Vol_" + volumeTOs.get(index).getId() +
"_Snapshot", pathWithScaleIOVolumeName, false));
+ String pathWithScaleIOVolumeName =
ScaleIOUtil.updatedPathWithVolumeName(snapshotVolumeId, snapshotName);
+ vmSnapshotDetails.add(new
VMSnapshotDetailsVO(vmSnapshot.getId(),
+ "Vol_" + matchedVolume.getId() + "_Snapshot",
+ pathWithScaleIOVolumeName, false));
}
vmSnapshotDetailsDao.saveDetails(vmSnapshotDetails);
diff --git
a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
index c6a61c35b8b..8e23bc159a4 100644
---
a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
+++
b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java
@@ -94,6 +94,9 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
private String password;
private String sessionKey;
+ private String gatewayVersion = null;
+ private int[] parsedVersion = null;
+
// The session token is valid for 8 hours from the time it was created,
unless there has been no activity for 10 minutes
// Reference:
https://cpsdocs.dellemc.com/bundle/PF_REST_API_RG/page/GUID-92430F19-9F44-42B6-B898-87D5307AE59B.html
private static final long MAX_VALID_SESSION_TIME_IN_HRS = 8;
@@ -621,15 +624,26 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
throw new CloudRuntimeException("Unable to revert, source snapshot
volume and destination volume doesn't belong to same volume tree");
}
+ String requestBody =
buildOverwriteVolumeContentRequest(sourceSnapshotVolumeId);
+
Boolean overwriteVolumeContentStatus = post(
"/instances/Volume::" + destVolumeId +
"/action/overwriteVolumeContent",
-
String.format("{\"srcVolumeId\":\"%s\",\"allowOnExtManagedVol\":\"TRUE\"}",
sourceSnapshotVolumeId), Boolean.class);
+ requestBody, Boolean.class);
if (overwriteVolumeContentStatus != null) {
return overwriteVolumeContentStatus;
}
return false;
}
+ private String buildOverwriteVolumeContentRequest(final String
srcVolumeId) {
+ if (isVersionAtLeast(4, 0)) {
+ logger.debug("Using PowerFlex 4.0+ overwriteVolumeContent request
body");
+ return String.format("{\"srcVolumeId\":\"%s\"}", srcVolumeId);
+ } else {
+ logger.debug("Using pre-4.0 overwriteVolumeContent request body");
+ return
String.format("{\"srcVolumeId\":\"%s\",\"allowOnExtManagedVol\":\"TRUE\"}",
srcVolumeId); }
+ }
+
@Override
public boolean mapVolumeToSdc(final String volumeId, final String sdcId) {
Preconditions.checkArgument(StringUtils.isNotEmpty(volumeId), "Volume
id cannot be null");
@@ -1168,4 +1182,49 @@ public class ScaleIOGatewayClientImpl implements
ScaleIOGatewayClient {
sb.append("\n");
return sb.toString();
}
+
+ private String fetchGatewayVersion() {
+ try {
+ JsonNode node = get("/version", JsonNode.class);
+ if (node != null && node.isTextual()) {
+ return node.asText();
+ }
+ if (node != null && node.has("version")) {
+ return node.get("version").asText();
+ }
+ } catch (Exception e) {
+ logger.warn("Could not fetch PowerFlex gateway version: " +
e.getMessage());
+ }
+ return null;
+ }
+
+ private int[] parseVersion(String version) {
+ if (StringUtils.isEmpty(version)) return new int[]{0, 0, 0};
+ String[] parts = version.replaceAll("\"", "").split("\\.");
+ int[] parsed = new int[3];
+ for (int i = 0; i < Math.min(parts.length, 3); i++) {
+ try {
+ parsed[i] = Integer.parseInt(parts[i].trim());
+ } catch (NumberFormatException e) {
+ parsed[i] = 0;
+ }
+ }
+ return parsed;
+ }
+
+ private synchronized int[] getGatewayVersion() {
+ if (parsedVersion == null) {
+ gatewayVersion = fetchGatewayVersion();
+ parsedVersion = parseVersion(gatewayVersion);
+ logger.info("PowerFlex Gateway version detected: " + gatewayVersion
+ + " => parsed: " + Arrays.toString(parsedVersion));
+ }
+ return parsedVersion;
+ }
+
+ private boolean isVersionAtLeast(int major, int minor) {
+ int[] v = getGatewayVersion();
+ if (v[0] != major) return v[0] > major;
+ return v[1] >= minor;
+ }
}