Skip to content

Commit a3a0f40

Browse files
committed
linstor: Fix using multiple primary storage with same linstor-controller (apache#10280)
1 parent ed95144 commit a3a0f40

4 files changed

Lines changed: 459 additions & 49 deletions

File tree

plugins/storage/volume/linstor/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to Linstor CloudStack plugin will be documented in this file
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2025-01-27]
9+
10+
### Fixed
11+
12+
- Use of multiple primary storages on the same linstor controller
13+
814
## [2025-01-20]
915

1016
### Fixed

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

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
import org.libvirt.LibvirtException;
6161

6262
import java.io.File;
63+
import java.io.FileInputStream;
64+
import java.io.IOException;
65+
import java.nio.file.Files;
66+
import java.nio.file.Path;
67+
import java.nio.file.Paths;
6368

6469
@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor)
6570
public class LinstorStorageAdaptor implements StorageAdaptor {
@@ -198,10 +203,10 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
198203
final DevelopersApi api = getLinstorAPI(pool);
199204

200205
try {
201-
List<ResourceDefinition> definitionList = api.resourceDefinitionList(
202-
Collections.singletonList(rscName), null, null, null);
206+
ResourceDefinition resourceDefinition = LinstorUtil.findResourceDefinition(
207+
api, rscName, lpool.getResourceGroup());
203208

204-
if (definitionList.isEmpty()) {
209+
if (resourceDefinition == null) {
205210
ResourceGroupSpawn rgSpawn = new ResourceGroupSpawn();
206211
rgSpawn.setResourceDefinitionName(rscName);
207212
rgSpawn.addVolumeSizesItem(size / 1024); // linstor uses KiB
@@ -211,22 +216,28 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
211216
handleLinstorApiAnswers(answers, "Linstor: Unable to spawn resource.");
212217
}
213218

219+
String foundRscName = resourceDefinition != null ? resourceDefinition.getName() : rscName;
220+
214221
// query linstor for the device path
215222
List<ResourceWithVolumes> resources = api.viewResources(
216223
Collections.emptyList(),
217-
Collections.singletonList(rscName),
224+
Collections.singletonList(foundRscName),
218225
Collections.emptyList(),
219226
null,
220227
null,
221228
null);
222229

223-
makeResourceAvailable(api, rscName, false);
230+
makeResourceAvailable(api, foundRscName, false);
224231

225232
if (!resources.isEmpty() && !resources.get(0).getVolumes().isEmpty()) {
226233
final String devPath = resources.get(0).getVolumes().get(0).getDevicePath();
227234
s_logger.info("Linstor: Created drbd device: " + devPath);
228235
final KVMPhysicalDisk kvmDisk = new KVMPhysicalDisk(devPath, name, pool);
229236
kvmDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW);
237+
long allocatedKib = resources.get(0).getVolumes().get(0).getAllocatedSizeKib() != null ?
238+
resources.get(0).getVolumes().get(0).getAllocatedSizeKib() : 0;
239+
kvmDisk.setSize(allocatedKib >= 0 ? allocatedKib * 1024 : 0);
240+
kvmDisk.setVirtualSize(size);
230241
return kvmDisk;
231242
} else {
232243
s_logger.error("Linstor: viewResources didn't return resources or volumes.");
@@ -470,21 +481,56 @@ public boolean disconnectPhysicalDiskByPath(String localPath)
470481
return false;
471482
}
472483

484+
/**
485+
* Decrements the aux property key for template resource and deletes or just deletes if not template resource.
486+
* @param api
487+
* @param rscName
488+
* @param rscGrpName
489+
* @return
490+
* @throws ApiException
491+
*/
492+
private boolean deRefOrDeleteResource(DevelopersApi api, String rscName, String rscGrpName) throws ApiException {
493+
boolean deleted = false;
494+
List<ResourceDefinition> existingRDs = LinstorUtil.getRDListStartingWith(api, rscName);
495+
for (ResourceDefinition rd : existingRDs) {
496+
int expectedProps = 0; // if it is a non template resource, we don't expect any _cs-template-for- prop
497+
String propKey = LinstorUtil.getTemplateForAuxPropKey(rscGrpName);
498+
if (rd.getProps().containsKey(propKey)) {
499+
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
500+
rdm.deleteProps(Collections.singletonList(propKey));
501+
api.resourceDefinitionModify(rd.getName(), rdm);
502+
expectedProps = 1;
503+
}
504+
505+
// if there is only one template-for property left for templates, the template isn't needed anymore
506+
// or if it isn't a template anyway, it will not have this Aux property
507+
// _cs-template-for- poperties work like a ref-count.
508+
if (rd.getProps().keySet().stream()
509+
.filter(key -> key.startsWith("Aux/" + LinstorUtil.CS_TEMPLATE_FOR_PREFIX))
510+
.count() == expectedProps) {
511+
ApiCallRcList answers = api.resourceDefinitionDelete(rd.getName());
512+
checkLinstorAnswersThrow(answers);
513+
deleted = true;
514+
}
515+
}
516+
return deleted;
517+
}
518+
473519
@Override
474520
public boolean deletePhysicalDisk(String name, KVMStoragePool pool, Storage.ImageFormat format)
475521
{
476522
s_logger.debug("Linstor: deletePhysicalDisk " + name);
477523
final DevelopersApi api = getLinstorAPI(pool);
524+
final String rscName = getLinstorRscName(name);
525+
final LinstorStoragePool linstorPool = (LinstorStoragePool) pool;
526+
String rscGrpName = linstorPool.getResourceGroup();
478527

479528
try {
480-
final String rscName = getLinstorRscName(name);
481-
s_logger.debug("Linstor: delete resource definition " + rscName);
482-
ApiCallRcList answers = api.resourceDefinitionDelete(rscName);
483-
handleLinstorApiAnswers(answers, "Linstor: Unable to delete resource definition " + rscName);
529+
return deRefOrDeleteResource(api, rscName, rscGrpName);
484530
} catch (ApiException apiEx) {
531+
s_logger.error("Linstor: ApiEx - " + apiEx.getMessage());
485532
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
486533
}
487-
return true;
488534
}
489535

490536
@Override
@@ -552,6 +598,56 @@ private boolean resourceSupportZeroBlocks(KVMStoragePool destPool, String resNam
552598
return false;
553599
}
554600

601+
/**
602+
* Checks if the given disk is the SystemVM template, by checking its properties file in the same directory.
603+
* The initial systemvm template resource isn't created on the management server, but
604+
* we now need to know if the systemvm template is used, while copying.
605+
* @param disk
606+
* @return True if it is the systemvm template disk, else false.
607+
*/
608+
private static boolean isSystemTemplate(KVMPhysicalDisk disk) {
609+
Path diskPath = Paths.get(disk.getPath());
610+
Path propFile = diskPath.getParent().resolve("template.properties");
611+
if (Files.exists(propFile)) {
612+
java.util.Properties templateProps = new java.util.Properties();
613+
try {
614+
templateProps.load(new FileInputStream(propFile.toFile()));
615+
String desc = templateProps.getProperty("description");
616+
if (desc.startsWith("SystemVM Template")) {
617+
return true;
618+
}
619+
} catch (IOException e) {
620+
return false;
621+
}
622+
}
623+
return false;
624+
}
625+
626+
/**
627+
* Conditionally sets the correct aux properties for templates or basic resources.
628+
* @param api
629+
* @param srcDisk
630+
* @param destPool
631+
* @param name
632+
*/
633+
private void setRscDfnAuxProperties(
634+
DevelopersApi api, KVMPhysicalDisk srcDisk, KVMStoragePool destPool, String name) {
635+
// if it is the initial systemvm disk copy, we need to apply the _cs-template-for property.
636+
if (isSystemTemplate(srcDisk)) {
637+
applyAuxProps(api, name, "SystemVM Template", null);
638+
LinstorStoragePool linPool = (LinstorStoragePool) destPool;
639+
final String rscName = getLinstorRscName(name);
640+
try {
641+
LinstorUtil.setAuxTemplateForProperty(api, rscName, linPool.getResourceGroup());
642+
} catch (ApiException apiExc) {
643+
s_logger.error(String.format("Error setting aux template for property for %s", rscName));
644+
logLinstorAnswers(apiExc.getApiCallRcList());
645+
}
646+
} else {
647+
applyAuxProps(api, name, srcDisk.getDispName(), srcDisk.getVmName());
648+
}
649+
}
650+
555651
@Override
556652
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout)
557653
{
@@ -566,7 +662,7 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
566662
name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.FAT, disk.getVirtualSize());
567663

568664
final DevelopersApi api = getLinstorAPI(destPools);
569-
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
665+
setRscDfnAuxProperties(api, disk, destPools, name);
570666

571667
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
572668
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());

0 commit comments

Comments
 (0)