Skip to content

CSTACKEX-262: Support for Compute + Volume migration and Volume migration - #112

Open
sandeeplocharla wants to merge 17 commits into
mainfrom
feature/CSTACKEX-262
Open

sandeeplocharla wants to merge 17 commits into
mainfrom
feature/CSTACKEX-262

Conversation

@sandeeplocharla

@sandeeplocharla sandeeplocharla commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Description

This PR...

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial
Use case Suite Tests Result
Live VM migration with volumes migration_live 22 Pass
Stopped VM storage migration migration_stopped 15 Pass
Volume migration (detached or stopped-attached) migration_volume 23 Pass
iSCSI pool lifecycle iscsi_pool_lifecycle 8 Pass
iSCSI pool with volumes iscsi_pool_volumes 7 Pass
iSCSI zone-scoped pool iscsi_zone_pool 4 Pass
iSCSI volume lifecycle iscsi_volume 5 Pass
iSCSI VM volume attach iscsi_instance 8 Pass
NFS3 pool lifecycle nfs3_pool_lifecycle 8 Pass
NFS3 pool with volumes nfs3_pool_volumes 7 Pass
NFS3 zone-scoped pool nfs3_zone_pool 4 Pass
NFS3 volume lifecycle nfs3_volume 5 Pass
NFS3 VM volume attach nfs3_instance 8 Pass

Live VM with volumes (migrateVirtualMachineWithVolume)

Test What it covers Status
test_01_nfs3_default_primary_to_cluster DefaultPrimary root and data volumes to NFS3 C1a Pass
test_02_nfs3_cluster_to_cluster NFS3 C1a to C1b Pass
test_03_nfs3_cluster_to_zone NFS3 C1b to zone-wide Z1 Pass
test_04_nfs3_zone_to_zone NFS3 Z1 to Z2 Pass
test_05_nfs3_zone_to_cluster NFS3 Z2 back to C1a Pass
test_06_iscsi_default_primary_to_cluster DefaultPrimary root and data volumes to iSCSI C1a Pass
test_07_iscsi_cluster_to_cluster iSCSI C1a to C1b Pass
test_08_iscsi_cluster_to_zone iSCSI C1b to Z1 Pass
test_09_iscsi_zone_to_zone iSCSI Z1 to Z2 Pass
test_10_iscsi_zone_to_cluster iSCSI Z2 back to C1a Pass
test_11_reject_nfs3_to_iscsi Live NFS3-to-iSCSI mapping is rejected Pass
test_12_reject_iscsi_to_nfs3 Live iSCSI-to-NFS3 mapping is rejected Pass
test_13_reject_live_volume_only migrateVolume with livemigrate on a running volume is rejected Pass
test_14_nfs3_cluster_to_cluster After host split, NFS3 C1a to C2 Pass
test_15_nfs3_cluster_to_zone NFS3 C1a to Z1 onto a C2 host Pass
test_16_nfs3_zone_to_cluster NFS3 Z1 to C2 Pass
test_17_nfs3_zone_to_zone NFS3 Z1 to Z2 onto a C2 host Pass
test_18_iscsi_cluster_to_cluster iSCSI C1a to C2 onto a C2 host Pass
test_19_iscsi_cluster_to_zone iSCSI C1a to Z1 onto a C2 host Pass
test_20_iscsi_zone_to_cluster iSCSI Z1 to C2 Pass
test_21_iscsi_zone_to_zone iSCSI Z1 to Z2 onto a C2 host Pass
test_22_reject_pool_not_reachable_from_destination_host Cluster-1 pool mapped to a cluster-2 host is rejected Pass

Stopped VM storage migration (migrateVirtualMachineWithVolume, no destination host)

Test What it covers Status
test_01_nfs3_default_primary_to_cluster DefaultPrimary to NFS3 C1a Pass
test_02_nfs3_cluster_to_cluster_same_cluster NFS3 between cluster pools in the same cluster Pass
test_03_nfs3_cluster_to_cluster_across_clusters NFS3 to a pool in the second cluster Pass
test_04_nfs3_cluster_to_zone NFS3 cluster pool to zone-wide Pass
test_05_nfs3_zone_to_zone NFS3 between two zone-wide pools Pass
test_06_nfs3_zone_to_cluster NFS3 zone-wide back to C1a Pass
test_07_iscsi_default_primary_to_cluster DefaultPrimary to iSCSI C1a Pass
test_08_iscsi_cluster_to_cluster_same_cluster iSCSI between cluster pools in the same cluster Pass
test_09_iscsi_cluster_to_cluster_across_clusters iSCSI to a pool in the second cluster Pass
test_10_iscsi_cluster_to_zone iSCSI cluster pool to zone-wide Pass
test_11_iscsi_zone_to_zone iSCSI between two zone-wide pools Pass
test_12_iscsi_zone_to_cluster iSCSI zone-wide back to C1a Pass
test_13_reject_nfs3_to_iscsi Stopped-VM NFS3-to-iSCSI is rejected Pass
test_14_reject_iscsi_to_nfs3 Stopped-VM iSCSI-to-NFS3 is rejected Pass
test_15_reject_running_vm Stopped-VM API form is rejected once the VM is running Pass

Volume migration (migrateVolume)

Test What it covers Status
test_01_nfs3_detached_default_primary_to_cluster Detached NFS3, DefaultPrimary to C1a Pass
test_02_nfs3_detached_cluster_to_cluster_same_cluster Detached NFS3, C1a to C1b Pass
test_03_nfs3_detached_cluster_to_cluster_across_clusters Detached NFS3, C1b to C2 Pass
test_04_nfs3_detached_cluster_to_zone Detached NFS3, C2 to Z1 Pass
test_05_nfs3_detached_zone_to_zone Detached NFS3, Z1 to Z2 Pass
test_06_nfs3_detached_zone_to_cluster Detached NFS3, Z2 back to C1a Pass
test_07_iscsi_detached_default_primary_to_cluster Detached iSCSI, DefaultPrimary to C1a Pass
test_08_iscsi_detached_cluster_to_cluster_same_cluster Detached iSCSI, C1a to C1b Pass
test_09_iscsi_detached_cluster_to_cluster_across_clusters Detached iSCSI, C1b to C2 Pass
test_10_iscsi_detached_cluster_to_zone Detached iSCSI, C2 to Z1 Pass
test_11_iscsi_detached_zone_to_zone Detached iSCSI, Z1 to Z2 Pass
test_12_iscsi_detached_zone_to_cluster Detached iSCSI, Z2 back to C1a Pass
test_13_nfs3_attached_stopped_vm_cluster_to_zone Stopped-attached NFS3 data, C1a to Z1; root stays Pass
test_14_iscsi_attached_stopped_vm_cluster_to_zone Stopped-attached iSCSI data, C1a to Z1; root stays Pass
test_15_nfs3_attached_stopped_vm_default_to_ontap Stopped-attached non-ONTAP data to ONTAP NFS3 Pass
test_16_iscsi_attached_stopped_vm_default_to_ontap Stopped-attached non-ONTAP data to ONTAP iSCSI Pass
test_17_reject_detached_nfs3_to_iscsi Detached NFS3-to-iSCSI is rejected Pass
test_18_reject_detached_iscsi_to_nfs3 Detached iSCSI-to-NFS3 is rejected Pass
test_19_reject_stopped_attached_nfs3_to_iscsi Stopped-attached NFS3-to-iSCSI is rejected Pass
test_20_reject_stopped_attached_iscsi_to_nfs3 Stopped-attached iSCSI-to-NFS3 is rejected Pass
test_21_reject_attached_running_vm_without_livemigrate Running-attached NFS3 without livemigrate is rejected Pass
test_22_reject_attached_running_vm_with_livemigrate Live volume-only migration is rejected Pass
test_23_reject_same_pool Migration to the volume's current pool is rejected Pass

@sandeeplocharla
sandeeplocharla marked this pull request as draft October 5, 2026 14:23
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

🔴 Test Coverage Grade: D — Marginal

Metric Value
Line coverage 24.86%
Branch coverage 18.99%

Grade Scale

Grade Line Coverage Meaning
🟢 A ≥ 80% Excellent - this code sleeps well at night 😴
🟡 B 60-79% Good - almost there, don't stop now 😉
🟠 C 40-59% Acceptable - your code is wearing a seatbelt, but no airbags 😬
🔴 D 20-39% Marginal - boldly shipping where no test has gone before 🖖
⛔ F < 20% Failing - tests? what tests? 🔥

Branch coverage is shown as a secondary signal. Grade is determined by line coverage.
View full Actions run

@sandeeplocharla
sandeeplocharla marked this pull request as ready for review October 7, 2026 01:02
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch.

@github-actions

Copy link
Copy Markdown

This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch.

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🔴 Test Coverage Grade: D — Marginal

Metric Value
Line coverage 25.96%
Branch coverage 20.26%

Grade Scale

Grade Line Coverage Meaning
🟢 A ≥ 80% Excellent - this code sleeps well at night 😴
🟡 B 60-79% Good - almost there, don't stop now 😉
🟠 C 40-59% Acceptable - your code is wearing a seatbelt, but no airbags 😬
🔴 D 20-39% Marginal - boldly shipping where no test has gone before 🖖
⛔ F < 20% Failing - tests? what tests? 🔥

Branch coverage is shown as a secondary signal. Grade is determined by line coverage.
View full Actions run

@github-actions

Copy link
Copy Markdown

This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch.

@piyush5netapp piyush5netapp left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving with some comments which can be taken in future.

@piyush5netapp

Copy link
Copy Markdown

Approving with some comments which can be taken in future.

Will wait for one comment reply on file LibvirtMigrateCommandWrapper

- LunMap: drop property-level NON_DEFAULT JsonInclude on logicalUnitNumber
- KVMStorageProcessor: prefer copy details over command options, narrow not-found handling to managed volumes
- OntapDataMotionStrategy: add disconnectVolumeFromHost helper used by failed and post-migration cleanup
- LibvirtMigrateCommandWrapper: remove the disconnect wrapper method and its test
- Add unit tests for the copy-details precedence and the new cleanup path
…ration QoS

Offline migration calls createAsync with a null callback and reads no result,
so a failed create must throw instead of returning silently. The post-migration
QoS reapply now retries twice more with a short wait, then logs and continues,
because the VM has already moved and a failed patch must not expunge its disks.
@sandeeplocharla sandeeplocharla self-assigned this Nov 11, 2026
* source volume's IOPS so the policy attached after {@code copy.source} matches the disk.
*/
private void copyQosIopsFromSourceWhenDestinationHasNone(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo) {
if (srcVolumeInfo == null || destVolumeInfo == null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

in what circumstances destination would have IOps available ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Destination by itself wouldn't have any existing IOPS. Cloudstack orchestration does a copy within the DB from source volume entity to destination and the IOPS is not one of the entries which get copied. So, in that case this method would set the source IOPS to destination volume. Also, in case of an exception cloudstack goes for a retry and this method would not go through copying the same again as the destination volume would have the details already.


success = true;
handlePostMigration(srcVolumeInfoToDestVolumeInfo, srcHost);
} catch (AgentUnavailableException | OperationTimedoutException | CloudRuntimeException ex) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

we should have another super set of exception handling to have smooth exit with errorMsg value assigned to the variable, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Gone through a bit in depth for this. From my understanding, there's only a DAO error from 311 which might not be under the purview of any of the exceptions mentioned. If it's a big issue like the DB is down completely, then the call shouldn't even reach here, but if its intermittent, then this workflow should go through in one of the API Cmd retries by cloudstack orchestration.
I feel adding a generic exception might not add much here, but please let me know your thoughts on this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

it is more about in worst cases also are we ensuring that product is not misbehaving and it remian in-sync with internal functionality and the responses coming out for user. It can be dao specific exception now, but it is good practice to handle cases for any probability.

@sandeeplocharla sandeeplocharla Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, will add it. This file has majority code borrowed from StorageSystemDataMotionStrategy class, so was only checking why they've added specific exceptions instead of handling 'Exception' overall.

Anyways, how do you think this should be handled?
Shall I replace the 'AgentUnavailableException | OperationTimedoutException | CloudRuntimeException' with just 'Exception', as all that catch block is doing is to log an error and throw a CloudRuntimeException with the errMsg.
If we add additional catch block for 'Exception' with similar code, it would look duplicate.

sendModifyTargetsCommand(getModifyTargetsCommand(storagePoolId, iqn, false), host);
}

private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String iqn, boolean add) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this method would help in cluster-to-cluster migration, but VM is being migrated from one host to another with in the same cluster then post migration, lun may be disconnected, can you validate this ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I've cross checked the automation tests for relevant tests. Tests 6-10 of suite 1 (test_01_live_vm_with_storage_migration) in the PR #113 verifies this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Are we not calling this method if migration is happening between the hosts from the same cluster ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, same cluster or different cluster, flow remains the same. The flow only changes based on whether there's a VM attached or not.

_volumeDao.update(volumeVO.getId(), volumeVO);
// Host-side rewrite clears NFS file QoS; re-attach from the destination volume's IOPS.
reapplyOntapQosAfterContentRewrite(destVolumeInfo);
} catch (Exception ex) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We should check for path for null, if it is not null then we should perform rollback by deleting the migrated object.

@sandeeplocharla sandeeplocharla Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The createAsync that runs from ln 162 would make sure that the (path != null) on success. In case of its failure, there's already handler in the finally.

FYI (Additional info)
Also, one thing to highlight, there's no rollback of the migrated volume in cloudstack orchestration, once the migration of volume is complete and there's some issue later. This is one of the major reasons why ontap related checks were added in core cloudstack code. The checks would verify the compatibility in migrating both ROOT and DATA disks, as this class would receive migration request per volume. Say, if ROOT migrated but DATA couldn't be due to compatibility issues, the ROOT volume wouldn't move back.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

My intention is to have this method level rollback. And in case of copy would have been created at the destination and post to that exception occurs, do we need to clean that storage object created at the destination in catch block?

I did not find desired code at line number 162 of this class. Yeah, you added the remove details from the cloudstacj database in recent commit, do we need to delete the storage object also if path is having value?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

ln 162 ==> 1destVolumeInfo.getDataStore().getDriver().createAsync(destVolumeInfo.getDataStore(), destVolumeInfo, null);`, I'm referring to this.

Yes, there's already rollback mechanism from the orchestration. If copyAsync fails then the finally method would pass the failure message back to the corresponding callback, triggering the cleanup.

So, even the additional scenario I added previously, say ROOT is successful while DATA failed, the DATA disk's rollback happens. Earlier I just wanted to highlight the overall flow.

The driver treated any unattached volume in Ready, Allocated or Creating state
as an offline migration destination, so an ordinary volume about to be attached
could get the migration-only host connect and NFS export policy update.

OntapDataMotionStrategy now tags the offline destination with the
ontap.migration.destination volume detail right after createAsync and clears it
in the finally block. The driver connects the host for migration only when the
volume is Migrating or carries that tag. The live path is unchanged because its
destination is already Migrating.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants