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
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
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;
Expand Down Expand Up @@ -200,11 +201,35 @@ public VMSnapshot takeVMSnapshot(VMSnapshot 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -621,15 +624,26 @@ public boolean revertSnapshot(final String sourceSnapshotVolumeId, final String
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); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is allowOnExtManagedVol parameter removed from 4.5 onwards?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

based on public Dell Rest API documentation, the parameter was removed starting on version 4.0, the previous version 3.6 contains the parameter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@owsferraro is volumeClass not required for 4.x call?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this parameter isn't required, but only have only tested on Powerflex 4.6

}

@Override
public boolean mapVolumeToSdc(final String volumeId, final String sdcId) {
Preconditions.checkArgument(StringUtils.isNotEmpty(volumeId), "Volume id cannot be null");
Expand Down Expand Up @@ -1168,4 +1182,49 @@ private String getConnectionManagerStats() {
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;
}
}
Loading