Linstor fix host picking (#12047)

This commit is contained in:
Rene Peinthor 2026-01-12 11:29:31 +01:00 committed by GitHub
parent 2b373a4659
commit 8dcfc7c767
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 55 additions and 46 deletions

View File

@ -63,6 +63,8 @@ import com.cloud.api.storage.LinstorBackupSnapshotCommand;
import com.cloud.api.storage.LinstorRevertBackupSnapshotCommand; import com.cloud.api.storage.LinstorRevertBackupSnapshotCommand;
import com.cloud.configuration.Config; import com.cloud.configuration.Config;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDao;
import com.cloud.resource.ResourceState; import com.cloud.resource.ResourceState;
import com.cloud.storage.DataStoreRole; import com.cloud.storage.DataStoreRole;
@ -921,9 +923,10 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
_backupsnapshotwait, _backupsnapshotwait,
VirtualMachineManager.ExecuteInSequence.value()); VirtualMachineManager.ExecuteInSequence.value());
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(linstorApi, rscName); final StoragePool pool = (StoragePool) volumeInfo.getDataStore();
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(linstorApi, pool, rscName);
if (optEP.isEmpty()) { if (optEP.isEmpty()) {
optEP = getLinstorEP(linstorApi, rscName); optEP = getLinstorEP(linstorApi, pool, rscName);
} }
if (optEP.isPresent()) { if (optEP.isPresent()) {
@ -1063,13 +1066,29 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
Answer answer = copyVolume(srcData, dstData); Answer answer = copyVolume(srcData, dstData);
res = new CopyCommandResult(null, answer); res = new CopyCommandResult(null, answer);
} else { } else {
Answer answer = new Answer(null, false, "noimpl"); throw new CloudRuntimeException("Not implemented for Linstor primary storage.");
res = new CopyCommandResult(null, answer);
res.setResult("Not implemented yet");
} }
callback.complete(res); callback.complete(res);
} }
private Host getEnabledClusterHost(StoragePool storagePool, List<String> linstorNodeNames) {
List<HostVO> csHosts;
if (storagePool.getClusterId() != null) {
csHosts = _hostDao.findByClusterId(storagePool.getClusterId());
} else {
csHosts = _hostDao.findByDataCenterId(storagePool.getDataCenterId());
}
Collections.shuffle(csHosts); // so we do not always pick the same host for operations
for (HostVO host : csHosts) {
if (host.getResourceState() == ResourceState.Enabled &&
host.getStatus() == Status.Up &&
linstorNodeNames.contains(host.getName())) {
return host;
}
}
return null;
}
/** /**
* Tries to get a Linstor cloudstack end point, that is at least diskless. * Tries to get a Linstor cloudstack end point, that is at least diskless.
* *
@ -1078,49 +1097,39 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
* @return Optional RemoteHostEndPoint if one could get found. * @return Optional RemoteHostEndPoint if one could get found.
* @throws ApiException * @throws ApiException
*/ */
private Optional<RemoteHostEndPoint> getLinstorEP(DevelopersApi api, String rscName) throws ApiException { private Optional<RemoteHostEndPoint> getLinstorEP(DevelopersApi api, StoragePool storagePool, String rscName)
throws ApiException {
List<String> linstorNodeNames = LinstorUtil.getLinstorNodeNames(api); List<String> linstorNodeNames = LinstorUtil.getLinstorNodeNames(api);
Collections.shuffle(linstorNodeNames); // do not always pick the first linstor node Host host = getEnabledClusterHost(storagePool, linstorNodeNames);
if (host != null) {
Host host = null; logger.info("Linstor: Make resource {} available on node {} ...", rscName, host.getName());
for (String nodeName : linstorNodeNames) { ApiCallRcList answers = api.resourceMakeAvailableOnNode(
host = _hostDao.findByName(nodeName); rscName, host.getName(), new ResourceMakeAvailable());
if (host != null && host.getResourceState() == ResourceState.Enabled) { if (answers.hasError()) {
logger.info(String.format("Linstor: Make resource %s available on node %s ...", rscName, nodeName)); logger.error("Linstor: Unable to make resource {} on node {} available: {}",
ApiCallRcList answers = api.resourceMakeAvailableOnNode(rscName, nodeName, new ResourceMakeAvailable()); rscName, host.getName(), LinstorUtil.getBestErrorMessage(answers));
if (!answers.hasError()) { return Optional.empty();
break; // found working host
} else { } else {
logger.error( return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
String.format("Linstor: Unable to make resource %s on node %s available: %s",
rscName,
nodeName,
LinstorUtil.getBestErrorMessage(answers)));
}
} }
} }
if (host == null)
{
logger.error("Linstor: Couldn't create a resource on any cloudstack host."); logger.error("Linstor: Couldn't create a resource on any cloudstack host.");
return Optional.empty(); return Optional.empty();
} }
else
{
return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
}
}
private Optional<RemoteHostEndPoint> getDiskfullEP(DevelopersApi api, String rscName) throws ApiException { private Optional<RemoteHostEndPoint> getDiskfullEP(DevelopersApi api, StoragePool storagePool, String rscName)
throws ApiException {
List<com.linbit.linstor.api.model.StoragePool> linSPs = LinstorUtil.getDiskfulStoragePools(api, rscName); List<com.linbit.linstor.api.model.StoragePool> linSPs = LinstorUtil.getDiskfulStoragePools(api, rscName);
if (linSPs != null) { if (linSPs != null) {
for (com.linbit.linstor.api.model.StoragePool sp : linSPs) { List<String> linstorNodeNames = linSPs.stream()
Host host = _hostDao.findByName(sp.getNodeName()); .map(com.linbit.linstor.api.model.StoragePool::getNodeName)
if (host != null && host.getResourceState() == ResourceState.Enabled) { .collect(Collectors.toList());
Host host = getEnabledClusterHost(storagePool, linstorNodeNames);
if (host != null) {
return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host)); return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
} }
} }
}
logger.error("Linstor: No diskfull host found."); logger.error("Linstor: No diskfull host found.");
return Optional.empty(); return Optional.empty();
} }
@ -1199,12 +1208,12 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
VirtualMachineManager.ExecuteInSequence.value()); VirtualMachineManager.ExecuteInSequence.value());
try { try {
Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, rscName); Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, pool, rscName);
if (optEP.isPresent()) { if (optEP.isPresent()) {
answer = optEP.get().sendMessage(cmd); answer = optEP.get().sendMessage(cmd);
} else { } else {
answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint.");
deleteResourceDefinition(pool, rscName); deleteResourceDefinition(pool, rscName);
throw new CloudRuntimeException("Unable to get matching Linstor endpoint.");
} }
} catch (ApiException exc) { } catch (ApiException exc) {
logger.error("copy template failed: ", exc); logger.error("copy template failed: ", exc);
@ -1241,12 +1250,12 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
Answer answer; Answer answer;
try { try {
Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, rscName); Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, pool, rscName);
if (optEP.isPresent()) { if (optEP.isPresent()) {
answer = optEP.get().sendMessage(cmd); answer = optEP.get().sendMessage(cmd);
} }
else { else {
answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint."); throw new CloudRuntimeException("Unable to get matching Linstor endpoint.");
} }
} catch (ApiException exc) { } catch (ApiException exc) {
logger.error("copy volume failed: ", exc); logger.error("copy volume failed: ", exc);
@ -1279,14 +1288,14 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
try { try {
String devName = restoreResourceFromSnapshot(api, pool, rscName, snapshotName, restoreName); String devName = restoreResourceFromSnapshot(api, pool, rscName, snapshotName, restoreName);
Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, restoreName); Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, pool, restoreName);
if (optEPAny.isPresent()) { if (optEPAny.isPresent()) {
// patch the src device path to the temporary linstor resource // patch the src device path to the temporary linstor resource
snapshotObject.setPath(devName); snapshotObject.setPath(devName);
origCmd.setSrcTO(snapshotObject.getTO()); origCmd.setSrcTO(snapshotObject.getTO());
answer = optEPAny.get().sendMessage(origCmd); answer = optEPAny.get().sendMessage(origCmd);
} else{ } else {
answer = new Answer(origCmd, false, "Unable to get matching Linstor endpoint."); throw new CloudRuntimeException("Unable to get matching Linstor endpoint.");
} }
} finally { } finally {
// delete the temporary resource, noop if already gone // delete the temporary resource, noop if already gone
@ -1348,7 +1357,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
VirtualMachineManager.ExecuteInSequence.value()); VirtualMachineManager.ExecuteInSequence.value());
cmd.setOptions(options); cmd.setOptions(options);
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, rscName); Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, pool, rscName);
Answer answer; Answer answer;
if (optEP.isPresent()) { if (optEP.isPresent()) {
answer = optEP.get().sendMessage(cmd); answer = optEP.get().sendMessage(cmd);