create temp CG for consistent VM snapshot for the VM which is span across multiple flexvolumes#74
Conversation
… multiple volumes at storage
… at multiple flexvolumes
…ective checks by which delete snapshot would be handled from primary only
|
testing results and all the executed test cases added under previous PR #67 |
sandeeplocharla
left a comment
There was a problem hiding this comment.
Partially reviewed. Still need to go through a good portion of OntapVMSnapshotStrategy.java and SnapshotManagerImpl.java
There was a problem hiding this comment.
Pull request overview
This PR enhances NetApp ONTAP managed-storage snapshot handling by ensuring VM snapshots remain consistent when a VM’s volumes span multiple FlexVols, and by preventing managed PRIMARY (array-native) volume snapshots from being incorrectly archived/bookkept as secondary snapshots.
Changes:
- Add a temporary ONTAP Consistency Group (CG) two-phase snapshot flow for VM snapshots spanning multiple FlexVols; keep a direct FlexVol snapshot fast-path for single-FlexVol VMs.
- Ensure managed PRIMARY volume snapshots (e.g., ONTAP volume snapshots) remain on primary/array storage and do not use secondary-archive bookkeeping paths.
- Extend ONTAP integration utilities/strategy to better separate “provisioning-time” aggregate validation from “operations-only” connectivity, and add CG REST client APIs + related unit tests.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java | Avoids archiving managed PRIMARY snapshots to secondary; refactors decision into archiveSnapshotToSecondary. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/vmsnapshot/OntapVMSnapshotStrategy.java | Implements temporary CG orchestration for multi-FlexVol VM snapshots, plus single-FlexVol direct snapshot path and improved naming. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java | Adds operations-only connect mode, job polling helpers, and centralized FlexVol snapshot delete orchestration. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/OntapStorageUtils.java | Adds snapshot naming helpers and an overload to connect with/without aggregate validation. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/OntapStorageConstants.java | Adds SVM UUID support and CG/SFSR/delete polling constants. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/lifecycle/OntapPrimaryDatastoreLifecycle.java | Persists resolved SVM UUID into pool details when available. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/client/SnapshotFeignClient.java | Adds ONTAP CG REST API endpoints (create/list CG, start/commit snapshot, delete CG). |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java | Delegates volume-snapshot delete/revert to strategy helpers; updates naming and makes delete more robust/idempotent. |
| plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/vmsnapshot/OntapVMSnapshotStrategyTest.java | Adds/updates unit tests for CG flow, naming, scope resolution, and avoids static utility connection issues. |
| plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/service/StorageStrategyTest.java | Adds tests for operations-only connect, job polling helpers, and FlexVol snapshot delete orchestration. |
| plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java | Updates capabilities expectations to include revert support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return null; | ||
| } | ||
|
|
||
| private String getStringField(Map<String, Object> record, String key) { |
There was a problem hiding this comment.
This can be in utils, instead.
| if (eligibleAggregates.isEmpty()) { | ||
| logger.error("No suitable aggregates found on SVM " + svmName + " for volume operations."); | ||
| throw new CloudRuntimeException("No suitable aggregates found on SVM " + svmName + " for volume operations."); | ||
| this.resolvedSvmUuid = svm.getUuid(); |
There was a problem hiding this comment.
Since we are fetching svm uuid now , lets explicitly add uuid in queryParams
There was a problem hiding this comment.
I did not get this comment
| VolumeDetailVO lunDetail = volumeDetailsDao.findDetail(volumeVO.getId(), OntapStorageConstants.LUN_DOT_UUID); | ||
| String lunUUID = lunDetail != null ? lunDetail.getValue() : null; | ||
| if (lunUUID == null) { | ||
| if (lunDetail.getValue() == null) { | ||
| throw new CloudRuntimeException("LUN UUID not found for iSCSI volume " + volumeVO.getId()); | ||
| } | ||
| lunUuid = lunDetail.getValue(); |
| /** | ||
| * Single GET attempt: try to match by name, | ||
| * then fall back to listing all CG snapshots in this group (And it would be one | ||
| * always since workflow is keep deleting the CG). | ||
| */ |
| String snapshotName = getSnapshotDetail(snapshotId, OntapStorageConstants.ONTAP_SNAP_NAME); | ||
| String poolIdStr = getSnapshotDetail(snapshotId, OntapStorageConstants.PRIMARY_POOL_ID); | ||
|
|
||
| if (flexVolUuid == null || ontapSnapshotUuid == null) { |
| logger.info("Selected aggregate: " + aggr.getName() + " for volume operations."); | ||
| this.aggregates = List.of(aggr); |
| public static String buildOntapSnapshotName(String cloudStackSnapshotName, String uniquenessSuffix) { | ||
| String normalizedBase = (cloudStackSnapshotName == null || cloudStackSnapshotName.trim().isEmpty()) | ||
| ? "snapshot" | ||
| : getOntapSnapshotName(cloudStackSnapshotName); | ||
| String suffix = (uniquenessSuffix == null || uniquenessSuffix.isEmpty()) | ||
| ? "" | ||
| : "_" + uniquenessSuffix.replaceAll("[^a-zA-Z0-9_]", "_"); | ||
| int maxLength = OntapStorageConstants.MAX_SNAPSHOT_NAME_LENGTH; | ||
| int maxBaseLength = maxLength - suffix.length(); | ||
| if (maxBaseLength <= 0) { | ||
| return normalizedBase.substring(0, maxLength); | ||
| } | ||
| if (normalizedBase.length() > maxBaseLength) { | ||
| normalizedBase = normalizedBase.substring(0, maxBaseLength); | ||
| } | ||
| return normalizedBase + suffix; | ||
| } |
| /** | ||
| * Single GET attempt: try to match by name, | ||
| * then fall back to listing all CG snapshots in this group (And it would be one | ||
| * always since workflow is keep deleting the CG). | ||
| */ |
| } else if (aggrResp.getSpace() == null || aggrResp.getAvailableBlockStorageSpace() == null || | ||
| aggrResp.getAvailableBlockStorageSpace() <= storage.getSize().doubleValue()) { | ||
| logger.warn("Aggregate " + aggr.getName() + " does not have sufficient available space. Skipping this aggregate."); | ||
| continue; | ||
| } | ||
| logger.info("Selected aggregate: " + aggr.getName() + " for volume operations."); | ||
| this.aggregates = List.of(aggr); | ||
| break; |
Description
This PR...
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
How did you try to break this feature and the system with this change?