This is an automated email from the ASF dual-hosted git repository.

DaanHoogland pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 68221cf1018 fix(linstor): pre-flight check destination is a LINSTOR 
satellite before live migration (#13077)
68221cf1018 is described below

commit 68221cf10182765c0abce1fe2e3954307335c19f
Author: James Peru Mmbono <[email protected]>
AuthorDate: Tue May 19 17:31:58 2026 +0300

    fix(linstor): pre-flight check destination is a LINSTOR satellite before 
live migration (#13077)
---
 .../storage/motion/LinstorDataMotionStrategy.java  | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/motion/LinstorDataMotionStrategy.java
 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/motion/LinstorDataMotionStrategy.java
index cab2820f09a..aa450a8290f 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/motion/LinstorDataMotionStrategy.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/motion/LinstorDataMotionStrategy.java
@@ -314,6 +314,58 @@ public class LinstorDataMotionStrategy implements 
DataMotionStrategy {
         return true;
     }
 
+    /**
+     * Verify that the destination KVM host is a registered LINSTOR satellite 
on the controller
+     * backing every destination pool involved in this migration. Throws 
CloudRuntimeException
+     * with a clear message when it isn't, instead of letting the resource 
creation later fail
+     * obscurely inside auto-placement.
+     *
+     * Best-effort: a transient controller error during this check does not 
block the migration
+     * — we log a warning and let the downstream resource-create surface the 
real issue. Only a
+     * confirmed "host not in node list" outcome aborts the migration up-front.
+     */
+    private void verifyDestinationIsLinstorSatellite(Map<VolumeInfo, 
DataStore> volumeDataStoreMap, Host destHost) {
+        if (destHost == null || destHost.getName() == null) {
+            // Without a destination host name to match, the only sensible 
thing is to let the
+            // existing flow run and report whatever it would have reported.
+            return;
+        }
+        for (Map.Entry<VolumeInfo, DataStore> entry : 
volumeDataStoreMap.entrySet()) {
+            DataStore destDataStore = entry.getValue();
+            StoragePoolVO destStoragePool = 
_storagePool.findById(destDataStore.getId());
+            if (destStoragePool == null
+                    || destStoragePool.getPoolType() != 
Storage.StoragePoolType.Linstor) {
+                continue;
+            }
+            DevelopersApi api = 
LinstorUtil.getLinstorAPI(destStoragePool.getHostAddress());
+            try {
+                List<String> nodes = LinstorUtil.getLinstorNodeNames(api);
+                if (nodes == null) {
+                    logger.warn("LINSTOR controller {} returned null node 
list; skipping pre-flight",
+                            destStoragePool.getHostAddress());
+                    return;
+                }
+                if (!nodes.contains(destHost.getName())) {
+                    throw new CloudRuntimeException(String.format(
+                            "Cannot migrate to host '%s': it is not a 
registered LINSTOR satellite on " +
+                                    "controller %s (pool '%s'). Known 
satellites: %s. Either register the " +
+                                    "host with `linstor node create` or pick a 
different destination.",
+                            destHost.getName(),
+                            destStoragePool.getHostAddress(),
+                            destStoragePool.getName(),
+                            nodes));
+                }
+            } catch (ApiException apiEx) {
+                // Don't block migration on a transient controller hiccup — 
log and let the
+                // downstream resource creation handle the real failure.
+                logger.warn("LINSTOR pre-flight check could not contact 
controller {}: {}; " +
+                                "letting downstream resource creation proceed",
+                        destStoragePool.getHostAddress(), 
apiEx.getBestMessage());
+                return;
+            }
+        }
+    }
+
     @Override
     public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, 
VirtualMachineTO vmTO, Host srcHost,
             Host destHost, AsyncCompletionCallback<CopyCommandResult> 
callback) {
@@ -323,6 +375,15 @@ public class LinstorDataMotionStrategy implements 
DataMotionStrategy {
                     String.format("Invalid hypervisor type [%s]. Only KVM 
supported", srcHost.getHypervisorType()));
         }
 
+        // Pre-flight: verify the destination KVM host is registered as a 
satellite on the
+        // LINSTOR controller backing each destination pool. Without this 
check, resource
+        // creation falls through to the resource-group's auto-placement 
filters and may
+        // either silently place the resource on the wrong node or fail with 
an opaque
+        // auto-place error from the LINSTOR API. Failing fast here gives 
operators a clear
+        // actionable message instead of having to correlate the 
live-migration failure with
+        // an unrelated LINSTOR controller log entry.
+        verifyDestinationIsLinstorSatellite(volumeDataStoreMap, destHost);
+
         String errMsg = null;
         VMInstanceVO vmInstance = _vmDao.findById(vmTO.getId());
         vmTO.setState(vmInstance.getState());

Reply via email to