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

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


The following commit(s) were added to refs/heads/4.18 by this push:
     new 371e244375b linstor: fix live migrate on non-hyperconverged setups 
(#9832)
371e244375b is described below

commit 371e244375be85f954ef9838da58f1eebaf8a8d7
Author: Rene Peinthor <rene.peint...@linbit.com>
AuthorDate: Thu Nov 7 10:16:32 2024 +0100

    linstor: fix live migrate on non-hyperconverged setups (#9832)
    
    In non-hyperconverged setups, diskless nodes don't have a connection
    to each other, so setting properties there had no effect.
    Now it is checked if a connection exists,
    between the live migration nodes and if not,
    it will set the allow-two-primaries on resource-definition level.
---
 .../kvm/storage/LinstorStorageAdaptor.java         | 78 +++++++++++++++++-----
 .../storage/datastore/util/LinstorUtil.java        | 25 +++++++
 2 files changed, 87 insertions(+), 16 deletions(-)

diff --git 
a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
 
b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
index 0ae65573d2b..424073e931f 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -53,6 +54,7 @@ import com.linbit.linstor.api.model.Resource;
 import com.linbit.linstor.api.model.ResourceConnectionModify;
 import com.linbit.linstor.api.model.ResourceDefinition;
 import com.linbit.linstor.api.model.ResourceGroup;
+import com.linbit.linstor.api.model.ResourceDefinitionModify;
 import com.linbit.linstor.api.model.ResourceGroupSpawn;
 import com.linbit.linstor.api.model.ResourceMakeAvailable;
 import com.linbit.linstor.api.model.ResourceWithVolumes;
@@ -257,6 +259,34 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
         }
     }
 
+    private void setAllowTwoPrimariesOnRD(DevelopersApi api, String rscName) 
throws ApiException {
+        ResourceDefinitionModify rdm = new ResourceDefinitionModify();
+        Properties props = new Properties();
+        props.put("DrbdOptions/Net/allow-two-primaries", "yes");
+        props.put("DrbdOptions/Net/protocol", "C");
+        rdm.setOverrideProps(props);
+        ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm);
+        if (answers.hasError()) {
+            s_logger.error(String.format("Unable to set protocol C and 
'allow-two-primaries' on %s", rscName));
+            // do not fail here as adding allow-two-primaries property is only 
a problem while live migrating
+        }
+    }
+
+    private void setAllowTwoPrimariesOnRc(DevelopersApi api, String rscName, 
String inUseNode) throws ApiException {
+        ResourceConnectionModify rcm = new ResourceConnectionModify();
+        Properties props = new Properties();
+        props.put("DrbdOptions/Net/allow-two-primaries", "yes");
+        props.put("DrbdOptions/Net/protocol", "C");
+        rcm.setOverrideProps(props);
+        ApiCallRcList answers = api.resourceConnectionModify(rscName, 
inUseNode, localNodeName, rcm);
+        if (answers.hasError()) {
+            s_logger.error(String.format(
+                    "Unable to set protocol C and 'allow-two-primaries' on 
%s/%s/%s",
+                    inUseNode, localNodeName, rscName));
+            // do not fail here as adding allow-two-primaries property is only 
a problem while live migrating
+        }
+    }
+
     /**
      * Checks if the given resource is in use by drbd on any host and
      * if so set the drbd option allow-two-primaries
@@ -268,17 +298,13 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
         String inUseNode = LinstorUtil.isResourceInUse(api, rscName);
         if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) {
             // allow 2 primaries for live migration, should be removed by 
disconnect on the other end
-            ResourceConnectionModify rcm = new ResourceConnectionModify();
-            Properties props = new Properties();
-            props.put("DrbdOptions/Net/allow-two-primaries", "yes");
-            props.put("DrbdOptions/Net/protocol", "C");
-            rcm.setOverrideProps(props);
-            ApiCallRcList answers = api.resourceConnectionModify(rscName, 
inUseNode, localNodeName, rcm);
-            if (answers.hasError()) {
-                s_logger.error(String.format(
-                        "Unable to set protocol C and 'allow-two-primaries' on 
%s/%s/%s",
-                        inUseNode, localNodeName, rscName));
-                // do not fail here as adding allow-two-primaries property is 
only a problem while live migrating
+
+            // if non hyperconverged setup, we have to set allow-two-primaries 
on the resource-definition
+            // as there is no resource connection between diskless nodes.
+            if (LinstorUtil.areResourcesDiskless(api, rscName, 
Arrays.asList(inUseNode, localNodeName))) {
+                setAllowTwoPrimariesOnRD(api, rscName);
+            } else {
+                setAllowTwoPrimariesOnRc(api, rscName, inUseNode);
             }
         }
     }
@@ -317,11 +343,22 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
         return true;
     }
 
-    private void removeTwoPrimariesRcProps(DevelopersApi api, String 
inUseNode, String rscName) throws ApiException {
+    private void removeTwoPrimariesRDProps(DevelopersApi api, String rscName, 
List<String> deleteProps)
+            throws ApiException {
+        ResourceDefinitionModify rdm = new ResourceDefinitionModify();
+        rdm.deleteProps(deleteProps);
+        ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm);
+        if (answers.hasError()) {
+            s_logger.error(
+                    String.format("Failed to remove 'protocol' and 
'allow-two-primaries' on %s: %s",
+                            rscName, 
LinstorUtil.getBestErrorMessage(answers)));
+            // do not fail here as removing allow-two-primaries property isn't 
fatal
+        }
+    }
+
+    private void removeTwoPrimariesRcProps(DevelopersApi api, String rscName, 
String inUseNode, List<String> deleteProps)
+            throws ApiException {
         ResourceConnectionModify rcm = new ResourceConnectionModify();
-        List<String> deleteProps = new ArrayList<>();
-        deleteProps.add("DrbdOptions/Net/allow-two-primaries");
-        deleteProps.add("DrbdOptions/Net/protocol");
         rcm.deleteProps(deleteProps);
         ApiCallRcList answers = api.resourceConnectionModify(rscName, 
localNodeName, inUseNode, rcm);
         if (answers.hasError()) {
@@ -334,6 +371,15 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
         }
     }
 
+    private void removeTwoPrimariesProps(DevelopersApi api, String inUseNode, 
String rscName) throws ApiException {
+        List<String> deleteProps = new ArrayList<>();
+        deleteProps.add("DrbdOptions/Net/allow-two-primaries");
+        deleteProps.add("DrbdOptions/Net/protocol");
+
+        removeTwoPrimariesRDProps(api, rscName, deleteProps);
+        removeTwoPrimariesRcProps(api, rscName, inUseNode, deleteProps);
+    }
+
     private boolean tryDisconnectLinstor(String volumePath, KVMStoragePool 
pool)
     {
         if (volumePath == null) {
@@ -367,7 +413,7 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
             try {
                 String inUseNode = LinstorUtil.isResourceInUse(api, 
rsc.getName());
                 if (inUseNode != null && 
!inUseNode.equalsIgnoreCase(localNodeName)) {
-                    removeTwoPrimariesRcProps(api, inUseNode, rsc.getName());
+                    removeTwoPrimariesProps(api, inUseNode, rsc.getName());
                 }
             } catch (ApiException apiEx) {
                 s_logger.error(apiEx.getBestMessage());
diff --git 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
index db2b5d1ea0b..bff25969d36 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.storage.datastore.util;
 
 import com.linbit.linstor.api.ApiClient;
+import com.linbit.linstor.api.ApiConsts;
 import com.linbit.linstor.api.ApiException;
 import com.linbit.linstor.api.DevelopersApi;
 import com.linbit.linstor.api.model.ApiCallRc;
@@ -27,8 +28,10 @@ import com.linbit.linstor.api.model.ResourceGroup;
 import com.linbit.linstor.api.model.ResourceWithVolumes;
 import com.linbit.linstor.api.model.StoragePool;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.log4j.Logger;
@@ -113,6 +116,28 @@ public class LinstorUtil {
         return null;
     }
 
+    /**
+     * Check if the given resources are diskless.
+     *
+     * @param api developer api object to use
+     * @param rscName resource name to check in use state.
+     * @return NodeName where the resource is inUse, if not in use `null`
+     * @throws ApiException forwards api errors
+     */
+    public static boolean areResourcesDiskless(DevelopersApi api, String 
rscName, Collection<String> nodeNames)
+            throws ApiException {
+        List<Resource> rscs = api.resourceList(rscName, null, null);
+        if (rscs != null) {
+            Collection<String> disklessNodes = rscs.stream()
+                .filter(rsc -> rsc.getFlags() != null && 
(rsc.getFlags().contains(ApiConsts.FLAG_DISKLESS) ||
+                        rsc.getFlags().contains(ApiConsts.FLAG_DRBD_DISKLESS)))
+                    .map(rsc -> rsc.getNodeName().toLowerCase())
+                    .collect(Collectors.toList());
+            return 
disklessNodes.containsAll(nodeNames.stream().map(String::toLowerCase).collect(Collectors.toList()));
+        }
+        return false;
+    }
+
     /**
      * Try to get the device path for the given resource name.
      * This could be made a bit more direct after java-linstor api is fixed 
for layer data subtypes.

Reply via email to