Skip to content

Commit 131ea9f

Browse files
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 <sureshkumar.anaparti@gmail.com> * Update plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com> * Update plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com> --------- Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>
1 parent c1af36f commit 131ea9f

File tree

2 files changed

+89
-5
lines changed

2 files changed

+89
-5
lines changed

engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
4141
import org.apache.cloudstack.storage.to.VolumeObjectTO;
4242
import org.apache.commons.collections.CollectionUtils;
43+
import org.apache.cloudstack.storage.datastore.api.Volume;
4344

4445
import com.cloud.agent.api.VMSnapshotTO;
4546
import com.cloud.alert.AlertManager;
@@ -200,11 +201,35 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
200201
if (volumeIds != null && !volumeIds.isEmpty()) {
201202
List<VMSnapshotDetailsVO> vmSnapshotDetails = new ArrayList<VMSnapshotDetailsVO>();
202203
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(), "SnapshotGroupId", snapshotGroupId, false));
204+
Map<String, String> snapshotNameToSrcPathMap = new HashMap<>();
205+
for (Map.Entry<String, String> entry : srcVolumeDestSnapshotMap.entrySet()) {
206+
snapshotNameToSrcPathMap.put(entry.getValue(), entry.getKey());
207+
}
208+
209+
for (String snapshotVolumeId : volumeIds) {
210+
// Use getVolume() to fetch snapshot volume details and get its name
211+
Volume snapshotVolume = client.getVolume(snapshotVolumeId);
212+
if (snapshotVolume == null) {
213+
throw new CloudRuntimeException("Cannot find snapshot volume with id: " + snapshotVolumeId);
214+
}
215+
String snapshotName = snapshotVolume.getName();
216+
217+
// Match back to source volume path
218+
String srcVolumePath = snapshotNameToSrcPathMap.get(snapshotName);
219+
if (srcVolumePath == null) {
220+
throw new CloudRuntimeException("Cannot match snapshot " + snapshotName + " to a source volume");
221+
}
222+
223+
// Find the matching VolumeObjectTO by path
224+
VolumeObjectTO matchedVolume = volumeTOs.stream()
225+
.filter(v -> ScaleIOUtil.getVolumePath(v.getPath()).equals(srcVolumePath))
226+
.findFirst()
227+
.orElseThrow(() -> new CloudRuntimeException("Cannot find source volume for path: " + srcVolumePath));
203228

204-
for (int index = 0; index < volumeIds.size(); index++) {
205-
String volumeSnapshotName = srcVolumeDestSnapshotMap.get(ScaleIOUtil.getVolumePath(volumeTOs.get(index).getPath()));
206-
String pathWithScaleIOVolumeName = ScaleIOUtil.updatedPathWithVolumeName(volumeIds.get(index), volumeSnapshotName);
207-
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(), "Vol_" + volumeTOs.get(index).getId() + "_Snapshot", pathWithScaleIOVolumeName, false));
229+
String pathWithScaleIOVolumeName = ScaleIOUtil.updatedPathWithVolumeName(snapshotVolumeId, snapshotName);
230+
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(),
231+
"Vol_" + matchedVolume.getId() + "_Snapshot",
232+
pathWithScaleIOVolumeName, false));
208233
}
209234

210235
vmSnapshotDetailsDao.saveDetails(vmSnapshotDetails);

plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ public class ScaleIOGatewayClientImpl implements ScaleIOGatewayClient {
9494
private String password;
9595
private String sessionKey;
9696

97+
private String gatewayVersion = null;
98+
private int[] parsedVersion = null;
99+
97100
// The session token is valid for 8 hours from the time it was created, unless there has been no activity for 10 minutes
98101
// Reference: https://cpsdocs.dellemc.com/bundle/PF_REST_API_RG/page/GUID-92430F19-9F44-42B6-B898-87D5307AE59B.html
99102
private static final long MAX_VALID_SESSION_TIME_IN_HRS = 8;
@@ -621,15 +624,26 @@ public boolean revertSnapshot(final String sourceSnapshotVolumeId, final String
621624
throw new CloudRuntimeException("Unable to revert, source snapshot volume and destination volume doesn't belong to same volume tree");
622625
}
623626

627+
String requestBody = buildOverwriteVolumeContentRequest(sourceSnapshotVolumeId);
628+
624629
Boolean overwriteVolumeContentStatus = post(
625630
"/instances/Volume::" + destVolumeId + "/action/overwriteVolumeContent",
626-
String.format("{\"srcVolumeId\":\"%s\",\"allowOnExtManagedVol\":\"TRUE\"}", sourceSnapshotVolumeId), Boolean.class);
631+
requestBody, Boolean.class);
627632
if (overwriteVolumeContentStatus != null) {
628633
return overwriteVolumeContentStatus;
629634
}
630635
return false;
631636
}
632637

638+
private String buildOverwriteVolumeContentRequest(final String srcVolumeId) {
639+
if (isVersionAtLeast(4, 0)) {
640+
logger.debug("Using PowerFlex 4.0+ overwriteVolumeContent request body");
641+
return String.format("{\"srcVolumeId\":\"%s\"}", srcVolumeId);
642+
} else {
643+
logger.debug("Using pre-4.0 overwriteVolumeContent request body");
644+
return String.format("{\"srcVolumeId\":\"%s\",\"allowOnExtManagedVol\":\"TRUE\"}", srcVolumeId); }
645+
}
646+
633647
@Override
634648
public boolean mapVolumeToSdc(final String volumeId, final String sdcId) {
635649
Preconditions.checkArgument(StringUtils.isNotEmpty(volumeId), "Volume id cannot be null");
@@ -1168,4 +1182,49 @@ private String getConnectionManagerStats() {
11681182
sb.append("\n");
11691183
return sb.toString();
11701184
}
1185+
1186+
private String fetchGatewayVersion() {
1187+
try {
1188+
JsonNode node = get("/version", JsonNode.class);
1189+
if (node != null && node.isTextual()) {
1190+
return node.asText();
1191+
}
1192+
if (node != null && node.has("version")) {
1193+
return node.get("version").asText();
1194+
}
1195+
} catch (Exception e) {
1196+
logger.warn("Could not fetch PowerFlex gateway version: " + e.getMessage());
1197+
}
1198+
return null;
1199+
}
1200+
1201+
private int[] parseVersion(String version) {
1202+
if (StringUtils.isEmpty(version)) return new int[]{0, 0, 0};
1203+
String[] parts = version.replaceAll("\"", "").split("\\.");
1204+
int[] parsed = new int[3];
1205+
for (int i = 0; i < Math.min(parts.length, 3); i++) {
1206+
try {
1207+
parsed[i] = Integer.parseInt(parts[i].trim());
1208+
} catch (NumberFormatException e) {
1209+
parsed[i] = 0;
1210+
}
1211+
}
1212+
return parsed;
1213+
}
1214+
1215+
private synchronized int[] getGatewayVersion() {
1216+
if (parsedVersion == null) {
1217+
gatewayVersion = fetchGatewayVersion();
1218+
parsedVersion = parseVersion(gatewayVersion);
1219+
logger.info("PowerFlex Gateway version detected: " + gatewayVersion
1220+
+ " => parsed: " + Arrays.toString(parsedVersion));
1221+
}
1222+
return parsedVersion;
1223+
}
1224+
1225+
private boolean isVersionAtLeast(int major, int minor) {
1226+
int[] v = getGatewayVersion();
1227+
if (v[0] != major) return v[0] > major;
1228+
return v[1] >= minor;
1229+
}
11711230
}

0 commit comments

Comments
 (0)