Skip to content

Commit d17325d

Browse files
committed
linstor/kvm: add support for ISO block devices and direct download
If Linstor storage pool is used, use the BLOCK qemu driver for the Linstor block device.
1 parent 352909f commit d17325d

File tree

7 files changed

+115
-35
lines changed

7 files changed

+115
-35
lines changed

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2982,6 +2982,17 @@ public String getVolumePath(final Connect conn, final DiskTO volume, boolean dis
29822982
return dataPath;
29832983
}
29842984

2985+
public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) {
2986+
return physicalDisk != null &&
2987+
physicalDisk.getPool().getType() == StoragePoolType.Linstor &&
2988+
physicalDisk.getFormat() != null &&
2989+
physicalDisk.getFormat()== PhysicalDiskFormat.RAW;
2990+
}
2991+
2992+
public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) {
2993+
return useBLOCKDiskType(physicalDisk) ? DiskDef.DiskType.BLOCK : DiskDef.DiskType.FILE;
2994+
}
2995+
29852996
public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException {
29862997
final Map<String, String> details = vmSpec.getDetails();
29872998
final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
@@ -3027,7 +3038,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
30273038
physicalDisk = getPhysicalDiskFromNfsStore(dataStoreUrl, data);
30283039
} else if (primaryDataStoreTO.getPoolType().equals(StoragePoolType.SharedMountPoint) ||
30293040
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Filesystem) ||
3030-
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool)) {
3041+
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool) ||
3042+
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Linstor)) {
30313043
physicalDisk = getPhysicalDiskPrimaryStore(primaryDataStoreTO, data);
30323044
}
30333045
}
@@ -3077,8 +3089,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
30773089
final DiskDef disk = new DiskDef();
30783090
int devId = volume.getDiskSeq().intValue();
30793091
if (volume.getType() == Volume.Type.ISO) {
3080-
3081-
disk.defISODisk(volPath, devId, isUefiEnabled);
3092+
final DiskDef.DiskType diskType = getDiskType(physicalDisk);
3093+
disk.defISODisk(volPath, devId, isUefiEnabled, diskType);
30823094

30833095
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
30843096
disk.setBusType(DiskDef.DiskBus.SCSI);
@@ -3170,7 +3182,7 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
31703182

31713183
if (vmSpec.getType() != VirtualMachine.Type.User) {
31723184
final DiskDef iso = new DiskDef();
3173-
iso.defISODisk(sysvmISOPath);
3185+
iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE);
31743186
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
31753187
iso.setBusType(DiskDef.DiskBus.SCSI);
31763188
}
@@ -3403,11 +3415,12 @@ public synchronized String attachOrDetachISO(final Connect conn, final String vm
34033415
final String name = isoPath.substring(index + 1);
34043416
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path);
34053417
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
3418+
final DiskDef.DiskType diskType = getDiskType(isoVol);
34063419
isoPath = isoVol.getPath();
34073420

3408-
iso.defISODisk(isoPath, diskSeq);
3421+
iso.defISODisk(isoPath, diskSeq, diskType);
34093422
} else {
3410-
iso.defISODisk(null, diskSeq);
3423+
iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
34113424
}
34123425

34133426
final String result = attachOrDetachDevice(conn, true, vmName, iso.toString());

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,10 @@ public boolean parseDomainXML(String domXML) {
126126
}
127127
def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
128128
} else if (device.equalsIgnoreCase("cdrom")) {
129-
def.defISODisk(diskFile, i+1, diskLabel);
129+
def.defISODisk(diskFile, i+1, diskLabel, DiskDef.DiskType.FILE);
130130
}
131131
} else if (type.equalsIgnoreCase("block")) {
132-
def.defBlockBasedDisk(diskDev, diskLabel,
133-
DiskDef.DiskBus.valueOf(bus.toUpperCase()));
132+
parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
134133
}
135134
if (StringUtils.isNotBlank(diskCacheMode)) {
136135
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
@@ -449,6 +448,25 @@ private static String getAttrValue(String tag, String attr, Element eElement) {
449448
return node.getAttribute(attr);
450449
}
451450

451+
/**
452+
* Parse the disk block part of the libvirt XML.
453+
* @param def
454+
* @param device
455+
* @param diskDev
456+
* @param diskLabel
457+
* @param bus
458+
* @param diskFile
459+
* @param curDiskIndex
460+
*/
461+
private void parseDiskBlock(DiskDef def, String device, String diskDev, String diskLabel, String bus,
462+
String diskFile, int curDiskIndex) {
463+
if (device.equalsIgnoreCase("disk")) {
464+
def.defBlockBasedDisk(diskDev, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()));
465+
} else if (device.equalsIgnoreCase("cdrom")) {
466+
def.defISODisk(diskFile, curDiskIndex+1, diskLabel, DiskDef.DiskType.BLOCK);
467+
}
468+
}
469+
452470
public Integer getVncPort() {
453471
return vncPort;
454472
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -833,8 +833,8 @@ public void defFileBasedDisk(String filePath, int devId, DiskFmtType diskFmtType
833833
}
834834
}
835835

836-
public void defISODisk(String volPath) {
837-
_diskType = DiskType.FILE;
836+
public void defISODisk(String volPath, DiskType diskType) {
837+
_diskType = diskType;
838838
_deviceType = DeviceType.CDROM;
839839
_sourcePath = volPath;
840840
_diskLabel = getDevLabel(3, DiskBus.IDE, true);
@@ -843,8 +843,8 @@ public void defISODisk(String volPath) {
843843
_bus = DiskBus.IDE;
844844
}
845845

846-
public void defISODisk(String volPath, boolean isUefiEnabled) {
847-
_diskType = DiskType.FILE;
846+
public void defISODisk(String volPath, boolean isUefiEnabled, DiskType diskType) {
847+
_diskType = diskType;
848848
_deviceType = DeviceType.CDROM;
849849
_sourcePath = volPath;
850850
_bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE;
@@ -853,18 +853,18 @@ public void defISODisk(String volPath, boolean isUefiEnabled) {
853853
_diskCacheMode = DiskCacheMode.NONE;
854854
}
855855

856-
public void defISODisk(String volPath, Integer devId) {
857-
defISODisk(volPath, devId, null);
856+
public void defISODisk(String volPath, Integer devId, DiskType diskType) {
857+
defISODisk(volPath, devId, null, diskType);
858858
}
859859

860-
public void defISODisk(String volPath, Integer devId, String diskLabel) {
860+
public void defISODisk(String volPath, Integer devId, String diskLabel, DiskType diskType) {
861861
if (devId == null && StringUtils.isBlank(diskLabel)) {
862862
s_logger.debug(String.format("No ID or label informed for volume [%s].", volPath));
863-
defISODisk(volPath);
863+
defISODisk(volPath, diskType);
864864
return;
865865
}
866866

867-
_diskType = DiskType.FILE;
867+
_diskType = diskType;
868868
_deviceType = DeviceType.CDROM;
869869
_sourcePath = volPath;
870870

@@ -881,11 +881,11 @@ public void defISODisk(String volPath, Integer devId, String diskLabel) {
881881
_bus = DiskBus.IDE;
882882
}
883883

884-
public void defISODisk(String volPath, Integer devId,boolean isSecure) {
884+
public void defISODisk(String volPath, Integer devId, boolean isSecure, DiskType diskType) {
885885
if (!isSecure) {
886-
defISODisk(volPath, devId);
886+
defISODisk(volPath, devId, diskType);
887887
} else {
888-
_diskType = DiskType.FILE;
888+
_diskType = diskType;
889889
_deviceType = DeviceType.CDROM;
890890
_sourcePath = volPath;
891891
_diskLabel = getDevLabel(devId, DiskBus.SATA, true);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,11 +1112,12 @@ protected synchronized void attachOrDetachISO(final Connect conn, final String v
11121112
storagePool = storagePoolMgr.getStoragePoolByURI(path);
11131113
}
11141114
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
1115+
final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol);
11151116
isoPath = isoVol.getPath();
11161117

1117-
iso.defISODisk(isoPath, isUefiEnabled);
1118+
iso.defISODisk(isoPath, isUefiEnabled, isoDiskType);
11181119
} else {
1119-
iso.defISODisk(null, isUefiEnabled);
1120+
iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE);
11201121
}
11211122

11221123
final List<DiskDef> disks = resource.getDisks(conn, vmName);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, S
170170
* Checks if downloaded template is extractable
171171
* @return true if it should be extracted, false if not
172172
*/
173-
private boolean isTemplateExtractable(String templatePath) {
173+
public static boolean isTemplateExtractable(String templatePath) {
174174
String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'");
175175
return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip");
176176
}
@@ -180,7 +180,7 @@ private boolean isTemplateExtractable(String templatePath) {
180180
* @param downloadedTemplateFile
181181
* @param templateUuid
182182
*/
183-
private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
183+
public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
184184
if (downloadedTemplateFile.endsWith(".zip")) {
185185
return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid;
186186
} else if (downloadedTemplateFile.endsWith(".bz2")) {
@@ -195,7 +195,7 @@ private String getExtractCommandForDownloadedFile(String downloadedTemplateFile,
195195
/**
196196
* Extract downloaded template into installPath, remove compressed file
197197
*/
198-
private void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
198+
public static void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
199199
String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile);
200200
Script.runSimpleBashScript(extractCommand);
201201
Script.runSimpleBashScript("rm -f " + downloadedTemplateFile);

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+
## [2024-10-14]
9+
10+
### Added
11+
12+
- Support for ISO direct download to primary storage
13+
814
## [2024-10-04]
915

1016
### Added

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

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import java.util.List;
2323
import java.util.Map;
2424
import java.util.Optional;
25+
import java.util.UUID;
2526

2627
import javax.annotation.Nonnull;
2728

2829
import com.cloud.storage.Storage;
2930
import com.cloud.utils.exception.CloudRuntimeException;
31+
import com.cloud.utils.script.Script;
3032

3133
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
3234
import org.apache.cloudstack.utils.qemu.QemuImg;
@@ -54,6 +56,8 @@
5456
import com.linbit.linstor.api.model.Volume;
5557
import com.linbit.linstor.api.model.VolumeDefinition;
5658

59+
import java.io.File;
60+
5761
@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor)
5862
public class LinstorStorageAdaptor implements StorageAdaptor {
5963
private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class);
@@ -517,13 +521,7 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
517521
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
518522

519523
final DevelopersApi api = getLinstorAPI(destPools);
520-
final String rscName = LinstorUtil.RSC_PREFIX + name;
521-
try {
522-
LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName());
523-
} catch (ApiException apiExc) {
524-
s_logger.error(String.format("Error setting aux properties for %s", rscName));
525-
logLinstorAnswers(apiExc.getApiCallRcList());
526-
}
524+
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
527525

528526
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
529527
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@@ -574,13 +572,57 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(
574572
return null;
575573
}
576574

575+
private void fileExistsOrThrow(String templateFilePath) {
576+
File sourceFile = new File(templateFilePath);
577+
if (!sourceFile.exists()) {
578+
throw new CloudRuntimeException("Direct download template file " + sourceFile +
579+
" does not exist on this host");
580+
}
581+
}
582+
583+
private String getFinalDirectDownloadPath(String templateFilePath, KVMStoragePool destPool) {
584+
String finalSourcePath = templateFilePath;
585+
if (LibvirtStorageAdaptor.isTemplateExtractable(templateFilePath)) {
586+
finalSourcePath = templateFilePath.substring(0, templateFilePath.lastIndexOf('.'));
587+
LibvirtStorageAdaptor.extractDownloadedTemplate(templateFilePath, destPool, finalSourcePath);
588+
}
589+
return finalSourcePath;
590+
}
591+
592+
private void applyAuxProps(DevelopersApi api, String csPath, String csName, String csVMName) {
593+
final String rscName = getLinstorRscName(csPath);
594+
try {
595+
LinstorUtil.applyAuxProps(api, rscName, csName, csVMName);
596+
} catch (ApiException apiExc) {
597+
s_logger.error(String.format("Error setting aux properties for %s", rscName));
598+
logLinstorAnswers(apiExc.getApiCallRcList());
599+
}
600+
}
601+
577602
@Override
578603
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
579604
KVMStoragePool destPool, Storage.ImageFormat format,
580605
int timeout)
581606
{
582-
s_logger.debug("Linstor: createTemplateFromDirectDownloadFile");
583-
return null;
607+
s_logger.debug(String.format("Linstor: createTemplateFromDirectDownloadFile: %s/%s", templateFilePath, format));
608+
fileExistsOrThrow(templateFilePath);
609+
String name = UUID.randomUUID().toString();
610+
611+
String finalSourcePath = getFinalDirectDownloadPath(templateFilePath, destPool);
612+
613+
File finalSourceFile = new File(finalSourcePath);
614+
final KVMPhysicalDisk dstDisk = destPool.createPhysicalDisk(
615+
name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, finalSourceFile.length(), null);
616+
617+
final DevelopersApi api = getLinstorAPI(destPool);
618+
applyAuxProps(api, name, finalSourceFile.getName(), null);
619+
620+
Script.runSimpleBashScript(
621+
String.format("dd if=\"%s\" of=\"%s\" bs=64k conv=nocreat,sparse oflag=direct",
622+
finalSourcePath, dstDisk.getPath()));
623+
624+
Script.runSimpleBashScript("rm " + finalSourcePath);
625+
return dstDisk;
584626
}
585627

586628
public long getCapacity(LinstorStoragePool pool) {

0 commit comments

Comments
 (0)