JoaoJandre commented on code in PR #12617:
URL: https://github.com/apache/cloudstack/pull/12617#discussion_r3382315110


##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -3150,6 +3158,11 @@ protected void migrate(final VMInstanceVO vm, final long 
srcHostId, final Deploy
         updateOverCommitRatioForVmProfile(profile, 
dest.getHost().getClusterId());
 
         final VirtualMachineTO to = toVmTO(profile);
+
+        if (vm.getHypervisorType() == HypervisorType.KVM && 
hasClvmVolumes(vm.getId())) {
+            executePreMigrationCommand(to, vm.getInstanceName(), srcHostId);
+        }

Review Comment:
   I think this check could be inside the executePreMigrationCommand, like it 
is done for the executePostMigrationCommand method.



##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -3320,6 +3334,29 @@ protected void migrate(final VMInstanceVO vm, final long 
srcHostId, final Deploy
         }
     }
 
+    private void executePostMigrationCommand(VMInstanceVO vm, VirtualMachineTO 
to, long dstHostId) {
+        if (vm.getHypervisorType() == HypervisorType.KVM && 
hasClvmVolumes(vm.getId())) {

Review Comment:
   We could invert the logic and return early, reducing indentation.



##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -3320,6 +3334,29 @@ protected void migrate(final VMInstanceVO vm, final long 
srcHostId, final Deploy
         }
     }
 
+    private void executePostMigrationCommand(VMInstanceVO vm, VirtualMachineTO 
to, long dstHostId) {
+        if (vm.getHypervisorType() == HypervisorType.KVM && 
hasClvmVolumes(vm.getId())) {
+            try {
+                logger.info("Executing post-migration tasks for VM {} with 
CLVM volumes on destination host {}", vm.getInstanceName(), dstHostId);

Review Comment:
   Could we log the host uuid instead?



##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -6441,6 +6507,32 @@ private Pair<Long, Long> 
findClusterAndHostIdForVm(VirtualMachine vm) {
         return findClusterAndHostIdForVm(vm, false);
     }
 
+    private boolean hasClvmVolumes(long vmId) {
+        List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
+        return volumes.stream()
+            .map(v -> _storagePoolDao.findById(v.getPoolId()))
+            .anyMatch(pool -> pool != null && 
ClvmPoolManager.isClvmPoolType(pool.getPoolType()));
+    }
+
+    private void executePreMigrationCommand(VirtualMachineTO to, String 
vmInstanceName, long srcHostId) {
+        logger.info("Sending PreMigrationCommand to source host {} for VM {} 
with CLVM volumes", srcHostId, vmInstanceName);

Review Comment:
   same about host uuid



##########
engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java:
##########
@@ -788,6 +805,121 @@ private String getVolumeIdentificationInfos(Volume 
volume) {
         return String.format("uuid: %s, name: %s", volume.getUuid(), 
volume.getName());
     }
 
+    /**
+     * Updates the CLVM_LOCK_HOST_ID for a migrated volume if applicable.
+     * For CLVM volumes that are attached to a VM, this updates the lock host 
tracking
+     * to point to the VM's current host after volume migration.
+     *
+     * @param migratedVolume The volume that was migrated
+     * @param destPool The destination storage pool
+     * @param operationType Description of the operation (e.g., "migrated", 
"live-migrated") for logging
+     */
+    private void updateClvmLockHostAfterMigration(Volume migratedVolume, 
StoragePool destPool, String operationType) {
+        if (migratedVolume == null || destPool == null) {
+            return;
+        }
+
+        StoragePoolVO pool = _storagePoolDao.findById(destPool.getId());
+        if (pool == null || 
!ClvmPoolManager.isClvmPoolType(pool.getPoolType())) {
+            return;
+        }
+
+        if (migratedVolume.getInstanceId() == null) {
+            return;
+        }
+
+        VMInstanceVO vm = 
vmInstanceDao.findById(migratedVolume.getInstanceId());
+        if (vm == null || vm.getHostId() == null) {
+            return;
+        }
+
+        clvmPoolManager.setClvmLockHostId(migratedVolume.getId(), 
vm.getHostId());
+        logger.debug("Updated CLVM_LOCK_HOST_ID for {} volume {} to host {} 
where VM {} is running",
+                operationType, migratedVolume.getUuid(), vm.getHostId(), 
vm.getInstanceName());
+    }
+
+    /**
+     * Retrieves the CLVM lock host ID from any existing volume of the 
specified VM.
+     * This is useful when attaching a new volume to a stopped VM - we want to 
maintain
+     * consistency by using the same host that manages the VM's other CLVM 
volumes.
+     *
+     * @param vmId The ID of the VM
+     * @return The host ID if found, null otherwise
+     */
+    private Long getClvmLockHostFromVmVolumes(Long vmId) {
+        if (vmId == null) {
+            return null;
+        }
+
+        List<VolumeVO> vmVolumes = _volsDao.findByInstance(vmId);
+        if (vmVolumes == null || vmVolumes.isEmpty()) {
+            return null;
+        }
+
+        for (VolumeVO volume : vmVolumes) {
+            if (volume.getPoolId() == null) {
+                continue;
+            }
+
+            StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId());
+            if (pool != null && 
ClvmPoolManager.isClvmPoolType(pool.getPoolType())) {

Review Comment:
   we could invert the logic to reduce indentation.



##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -3320,6 +3334,29 @@ protected void migrate(final VMInstanceVO vm, final long 
srcHostId, final Deploy
         }
     }
 
+    private void executePostMigrationCommand(VMInstanceVO vm, VirtualMachineTO 
to, long dstHostId) {
+        if (vm.getHypervisorType() == HypervisorType.KVM && 
hasClvmVolumes(vm.getId())) {
+            try {
+                logger.info("Executing post-migration tasks for VM {} with 
CLVM volumes on destination host {}", vm.getInstanceName(), dstHostId);
+                final PostMigrationCommand postMigrationCommand = new 
PostMigrationCommand(to, vm.getInstanceName());
+                final Answer postMigrationAnswer = _agentMgr.send(dstHostId, 
postMigrationCommand);
+
+                if (postMigrationAnswer == null || 
!postMigrationAnswer.getResult()) {
+                    final String details = postMigrationAnswer != null ? 
postMigrationAnswer.getDetails() : "null answer returned";
+                    logger.warn("Post-migration tasks failed for VM {} on 
destination host {}: {}. Migration completed but some cleanup may be needed.",
+                            vm.getInstanceName(), dstHostId, details);
+                } else {
+                    logger.info("Successfully completed post-migration tasks 
for VM {} on destination host {}", vm.getInstanceName(), dstHostId);
+                }
+            } catch (Exception e) {
+                logger.warn("Exception during post-migration tasks for VM {} 
on destination host {}: {}. Migration completed but some cleanup may be 
needed.",
+                        vm.getInstanceName(), dstHostId, e.getMessage(), e);
+            }
+        }
+
+        updateClvmLockHostForVmVolumes(vm.getId(), dstHostId);

Review Comment:
   If the result is false, or null, we still update the lock? How do we know if 
this update will be correct?
   If the host does not execute the command (the command queue is full and the 
command times out, or the host is restarted) we will update the lock metadata 
to point to the wrong host, correct?



##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -4922,6 +4980,14 @@ private void orchestrateMigrateForScale(final String 
vmUuid, final long srcHostI
         volumeMgr.prepareForMigration(profile, dest);
 
         final VirtualMachineTO to = toVmTO(profile);
+
+        // Step 1: Send PreMigrationCommand to source host to convert CLVM 
volumes to shared mode
+        // This must happen BEFORE PrepareForMigrationCommand on destination 
to avoid lock conflicts
+        if (vm.getHypervisorType() == HypervisorType.KVM && 
hasClvmVolumes(vm.getId())) {
+            executePreMigrationCommand(to, vm.getInstanceName(), srcHostId);

Review Comment:
   In this case we only execute the premigration, but not the post migration?



##########
engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java:
##########
@@ -2132,6 +2157,23 @@ public void copyAsync(Map<VolumeInfo, DataStore> 
volumeDataStoreMap, VirtualMach
                 throw new AgentUnavailableException("Operation timed out", 
destHost.getId());
             }
 
+            for (VolumeInfo vol : samePoolClvmVolumes) {

Review Comment:
   shouldn't we check if the pool is clvm before executing this for?



##########
engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java:
##########
@@ -2132,6 +2157,23 @@ public void copyAsync(Map<VolumeInfo, DataStore> 
volumeDataStoreMap, VirtualMach
                 throw new AgentUnavailableException("Operation timed out", 
destHost.getId());
             }
 
+            for (VolumeInfo vol : samePoolClvmVolumes) {
+                StoragePoolVO samePoolClvmPool = 
_storagePoolDao.findById(vol.getPoolId());
+                String vgName = samePoolClvmPool.getPath();
+                if (vgName.startsWith("/")) vgName = vgName.substring(1);

Review Comment:
   this `if` does not follow the java coding conventions for ACS.



##########
engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java:
##########
@@ -331,6 +336,27 @@ protected Answer copyVolumeFromSnapshot(DataObject 
snapObj, DataObject volObj) {
         }
     }
 
+    private void updateLockHostForVolume(EndPoint ep, DataObject volObj) {
+        if (ep != null && volObj instanceof VolumeInfo) {
+            VolumeInfo volumeInfo = (VolumeInfo) volObj;
+            StoragePool destPool = (StoragePool) volObj.getDataStore();
+            if (destPool != null && 
ClvmPoolManager.isClvmPoolType(destPool.getPoolType())) {

Review Comment:
   we could invert both of these logics to reduce indentation



##########
engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java:
##########
@@ -2211,19 +2253,56 @@ public void copyAsync(Map<VolumeInfo, DataStore> 
volumeDataStoreMap, VirtualMach
         }
     }
 
+    private void prepareDisksForMigrationForClvm(VirtualMachineTO vmTO, 
Map<VolumeInfo, DataStore> volumeDataStoreMap, Host srcHost) {
+        // For CLVM/CLVM_NG source pools, convert volumes from exclusive to 
shared mode
+        // on the source host BEFORE PrepareForMigrationCommand on the 
destination.
+        boolean hasClvmSource = volumeDataStoreMap.keySet().stream()
+                .map(v -> _storagePoolDao.findById(v.getPoolId()))
+                .anyMatch(p -> p != null && (p.getPoolType() == 
StoragePoolType.CLVM || p.getPoolType() == StoragePoolType.CLVM_NG));
+        if (hasClvmSource && srcHost.getHypervisorType() == 
HypervisorType.KVM) {
+            logger.info("CLVM/CLVM_NG source pool detected for VM [{}], 
sending PreMigrationCommand to source host [{}] to convert volumes to shared 
mode.", vmTO.getName(), srcHost.getId());
+            PreMigrationCommand preMigCmd = new PreMigrationCommand(vmTO, 
vmTO.getName());
+            try {
+                Answer preMigAnswer = agentManager.send(srcHost.getId(), 
preMigCmd);
+                if (preMigAnswer == null || !preMigAnswer.getResult()) {
+                    String details = preMigAnswer != null ? 
preMigAnswer.getDetails() : "null answer returned";
+                    logger.warn("PreMigrationCommand failed for CLVM/CLVM_NG 
VM [{}] on source host [{}]: {}. Migration will continue but may fail if 
volumes are exclusively locked.", vmTO.getName(), srcHost.getId(), details);

Review Comment:
   Shouldn't we abort the migration in this case?
   If we continue and it does not work, it could lead to problems in the VM, no?



##########
engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java:
##########
@@ -6441,6 +6507,32 @@ private Pair<Long, Long> 
findClusterAndHostIdForVm(VirtualMachine vm) {
         return findClusterAndHostIdForVm(vm, false);
     }
 
+    private boolean hasClvmVolumes(long vmId) {
+        List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
+        return volumes.stream()
+            .map(v -> _storagePoolDao.findById(v.getPoolId()))
+            .anyMatch(pool -> pool != null && 
ClvmPoolManager.isClvmPoolType(pool.getPoolType()));
+    }
+
+    private void executePreMigrationCommand(VirtualMachineTO to, String 
vmInstanceName, long srcHostId) {
+        logger.info("Sending PreMigrationCommand to source host {} for VM {} 
with CLVM volumes", srcHostId, vmInstanceName);
+        final PreMigrationCommand preMigCmd = new PreMigrationCommand(to, 
vmInstanceName);
+        Answer preMigAnswer = null;
+        try {
+            preMigAnswer = _agentMgr.send(srcHostId, preMigCmd);
+            if (preMigAnswer == null || !preMigAnswer.getResult()) {
+                final String details = preMigAnswer != null ? 
preMigAnswer.getDetails() : "null answer returned";

Review Comment:
   What will happen if the command was executed in the host, but the answer 
could not be delivered to the Management Server? Will the next try be able to 
repeat the process or will it be stuck ?



##########
engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageDataMotionStrategy.java:
##########
@@ -144,12 +144,16 @@ protected boolean 
isDestinationNfsPrimaryStorageClusterWide(Map<VolumeInfo, Data
     }
 
     /**
-     * Configures a {@link MigrateDiskInfo} object configured for migrating a 
File System volume and calls rootImageProvisioning.
+     * Configures a {@link MigrateDiskInfo} object configured for migrating a 
File System volume.
      */
     @Override
     protected MigrateCommand.MigrateDiskInfo 
configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath, String 
backingPath) {
-            return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(), 
MigrateCommand.MigrateDiskInfo.DiskType.FILE, 
MigrateCommand.MigrateDiskInfo.DriverType.QCOW2,
-                    MigrateCommand.MigrateDiskInfo.Source.FILE, destPath, 
backingPath);
+            return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(),
+                    MigrateCommand.MigrateDiskInfo.DiskType.FILE,
+                    MigrateCommand.MigrateDiskInfo.DriverType.QCOW2,
+                    MigrateCommand.MigrateDiskInfo.Source.FILE,
+                    destPath,
+                    backingPath);

Review Comment:
   Is this a style change only? Is it needed?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to