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 <[email protected]>
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.