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 4c4717b56abc..112037364a5b 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 @@ -17,6 +17,7 @@ package com.cloud.hypervisor.kvm.storage; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -49,6 +50,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.ResourceDefinitionModify; import com.linbit.linstor.api.model.ResourceGroupSpawn; import com.linbit.linstor.api.model.ResourceMakeAvailable; import com.linbit.linstor.api.model.ResourceWithVolumes; @@ -246,17 +248,33 @@ private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws Ap 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))) { + 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 + } + } else { + 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 + } } } } @@ -296,19 +314,34 @@ public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map 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()) { - s_logger.error( - String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s/%s/%s: %s", - localNodeName, - inUseNode, - rscName, LinstorUtil.getBestErrorMessage(answers))); - // do not fail here as removing allow-two-primaries property isn't fatal + + { + 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 + } + } + + { + ResourceConnectionModify rcm = new ResourceConnectionModify(); + rcm.deleteProps(deleteProps); + ApiCallRcList answers = api.resourceConnectionModify(rscName, localNodeName, inUseNode, rcm); + if (answers.hasError()) { + s_logger.error( + String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s/%s/%s: %s", + localNodeName, + inUseNode, + rscName, LinstorUtil.getBestErrorMessage(answers))); + // do not fail here as removing allow-two-primaries property isn't fatal + } } } 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 9aa6f151abc7..2dbfdea6d119 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; @@ -33,6 +34,7 @@ import javax.annotation.Nonnull; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -209,6 +211,28 @@ public static String isResourceInUse(DevelopersApi api, String rscName) throws A 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 nodeNames) + throws ApiException { + List rscs = api.resourceList(rscName, null, null); + if (rscs != null) { + Collection 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.