Skip to content

Commit d166f4a

Browse files
committed
Linstor: fix live migrate on non-hyperconverged setups
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.
1 parent 1af4158 commit d166f4a

File tree

2 files changed

+79
-21
lines changed

2 files changed

+79
-21
lines changed

plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.InputStreamReader;
2222

2323
import java.util.ArrayList;
24+
import java.util.Arrays;
2425
import java.util.Collections;
2526
import java.util.HashMap;
2627
import java.util.List;
@@ -53,6 +54,7 @@
5354
import com.linbit.linstor.api.model.ResourceConnectionModify;
5455
import com.linbit.linstor.api.model.ResourceDefinition;
5556
import com.linbit.linstor.api.model.ResourceGroup;
57+
import com.linbit.linstor.api.model.ResourceDefinitionModify;
5658
import com.linbit.linstor.api.model.ResourceGroupSpawn;
5759
import com.linbit.linstor.api.model.ResourceMakeAvailable;
5860
import com.linbit.linstor.api.model.ResourceWithVolumes;
@@ -268,17 +270,33 @@ private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws Ap
268270
String inUseNode = LinstorUtil.isResourceInUse(api, rscName);
269271
if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) {
270272
// allow 2 primaries for live migration, should be removed by disconnect on the other end
271-
ResourceConnectionModify rcm = new ResourceConnectionModify();
272-
Properties props = new Properties();
273-
props.put("DrbdOptions/Net/allow-two-primaries", "yes");
274-
props.put("DrbdOptions/Net/protocol", "C");
275-
rcm.setOverrideProps(props);
276-
ApiCallRcList answers = api.resourceConnectionModify(rscName, inUseNode, localNodeName, rcm);
277-
if (answers.hasError()) {
278-
s_logger.error(String.format(
279-
"Unable to set protocol C and 'allow-two-primaries' on %s/%s/%s",
280-
inUseNode, localNodeName, rscName));
281-
// do not fail here as adding allow-two-primaries property is only a problem while live migrating
273+
274+
// if non hyperconverged setup, we have to set allow-two-primaries on the resource-definition
275+
// as there is no resource connection between diskless nodes.
276+
if (LinstorUtil.areResourcesDiskless(api, rscName, Arrays.asList(inUseNode, localNodeName))) {
277+
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
278+
Properties props = new Properties();
279+
props.put("DrbdOptions/Net/allow-two-primaries", "yes");
280+
props.put("DrbdOptions/Net/protocol", "C");
281+
rdm.setOverrideProps(props);
282+
ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm);
283+
if (answers.hasError()) {
284+
s_logger.error(String.format("Unable to set protocol C and 'allow-two-primaries' on %s", rscName));
285+
// do not fail here as adding allow-two-primaries property is only a problem while live migrating
286+
}
287+
} else {
288+
ResourceConnectionModify rcm = new ResourceConnectionModify();
289+
Properties props = new Properties();
290+
props.put("DrbdOptions/Net/allow-two-primaries", "yes");
291+
props.put("DrbdOptions/Net/protocol", "C");
292+
rcm.setOverrideProps(props);
293+
ApiCallRcList answers = api.resourceConnectionModify(rscName, inUseNode, localNodeName, rcm);
294+
if (answers.hasError()) {
295+
s_logger.error(String.format(
296+
"Unable to set protocol C and 'allow-two-primaries' on %s/%s/%s",
297+
inUseNode, localNodeName, rscName));
298+
// do not fail here as adding allow-two-primaries property is only a problem while live migrating
299+
}
282300
}
283301
}
284302
}
@@ -318,19 +336,34 @@ public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<S
318336
}
319337

320338
private void removeTwoPrimariesRcProps(DevelopersApi api, String inUseNode, String rscName) throws ApiException {
321-
ResourceConnectionModify rcm = new ResourceConnectionModify();
322339
List<String> deleteProps = new ArrayList<>();
323340
deleteProps.add("DrbdOptions/Net/allow-two-primaries");
324341
deleteProps.add("DrbdOptions/Net/protocol");
325-
rcm.deleteProps(deleteProps);
326-
ApiCallRcList answers = api.resourceConnectionModify(rscName, localNodeName, inUseNode, rcm);
327-
if (answers.hasError()) {
328-
s_logger.error(
329-
String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s/%s/%s: %s",
330-
localNodeName,
331-
inUseNode,
332-
rscName, LinstorUtil.getBestErrorMessage(answers)));
333-
// do not fail here as removing allow-two-primaries property isn't fatal
342+
343+
{
344+
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
345+
rdm.deleteProps(deleteProps);
346+
ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm);
347+
if (answers.hasError()) {
348+
s_logger.error(
349+
String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s: %s",
350+
rscName, LinstorUtil.getBestErrorMessage(answers)));
351+
// do not fail here as removing allow-two-primaries property isn't fatal
352+
}
353+
}
354+
355+
{
356+
ResourceConnectionModify rcm = new ResourceConnectionModify();
357+
rcm.deleteProps(deleteProps);
358+
ApiCallRcList answers = api.resourceConnectionModify(rscName, localNodeName, inUseNode, rcm);
359+
if (answers.hasError()) {
360+
s_logger.error(
361+
String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s/%s/%s: %s",
362+
localNodeName,
363+
inUseNode,
364+
rscName, LinstorUtil.getBestErrorMessage(answers)));
365+
// do not fail here as removing allow-two-primaries property isn't fatal
366+
}
334367
}
335368
}
336369

plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.cloudstack.storage.datastore.util;
1818

1919
import com.linbit.linstor.api.ApiClient;
20+
import com.linbit.linstor.api.ApiConsts;
2021
import com.linbit.linstor.api.ApiException;
2122
import com.linbit.linstor.api.DevelopersApi;
2223
import com.linbit.linstor.api.model.ApiCallRc;
@@ -27,8 +28,10 @@
2728
import com.linbit.linstor.api.model.ResourceWithVolumes;
2829
import com.linbit.linstor.api.model.StoragePool;
2930

31+
import java.util.Collection;
3032
import java.util.Collections;
3133
import java.util.List;
34+
import java.util.stream.Collectors;
3235

3336
import com.cloud.utils.exception.CloudRuntimeException;
3437
import org.apache.log4j.Logger;
@@ -113,6 +116,28 @@ public static String isResourceInUse(DevelopersApi api, String rscName) throws A
113116
return null;
114117
}
115118

119+
/**
120+
* Check if the given resources are diskless.
121+
*
122+
* @param api developer api object to use
123+
* @param rscName resource name to check in use state.
124+
* @return NodeName where the resource is inUse, if not in use `null`
125+
* @throws ApiException forwards api errors
126+
*/
127+
public static boolean areResourcesDiskless(DevelopersApi api, String rscName, Collection<String> nodeNames)
128+
throws ApiException {
129+
List<Resource> rscs = api.resourceList(rscName, null, null);
130+
if (rscs != null) {
131+
Collection<String> disklessNodes = rscs.stream()
132+
.filter(rsc -> rsc.getFlags() != null && (rsc.getFlags().contains(ApiConsts.FLAG_DISKLESS) ||
133+
rsc.getFlags().contains(ApiConsts.FLAG_DRBD_DISKLESS)))
134+
.map(rsc -> rsc.getNodeName().toLowerCase())
135+
.collect(Collectors.toList());
136+
return disklessNodes.containsAll(nodeNames.stream().map(String::toLowerCase).collect(Collectors.toList()));
137+
}
138+
return false;
139+
}
140+
116141
/**
117142
* Try to get the device path for the given resource name.
118143
* This could be made a bit more direct after java-linstor api is fixed for layer data subtypes.

0 commit comments

Comments
 (0)