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());