Skip to content

Commit 122e14d

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 c1cbc50 commit 122e14d

File tree

7 files changed

+117
-35
lines changed

7 files changed

+117
-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
@@ -2983,6 +2983,17 @@ public String getVolumePath(final Connect conn, final DiskTO volume, boolean dis
29832983
return dataPath;
29842984
}
29852985

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

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

31723184
if (vmSpec.getType() != VirtualMachine.Type.User) {
31733185
final DiskDef iso = new DiskDef();
3174-
iso.defISODisk(sysvmISOPath);
3186+
iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE);
31753187
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
31763188
iso.setBusType(DiskDef.DiskBus.SCSI);
31773189
}
@@ -3414,11 +3426,12 @@ public synchronized String attachOrDetachISO(final Connect conn, final String vm
34143426
final String name = isoPath.substring(index + 1);
34153427
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path);
34163428
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
3429+
final DiskDef.DiskType diskType = getDiskType(isoVol);
34173430
isoPath = isoVol.getPath();
34183431

3419-
iso.defISODisk(isoPath, diskSeq);
3432+
iso.defISODisk(isoPath, diskSeq, diskType);
34203433
} else {
3421-
iso.defISODisk(null, diskSeq);
3434+
iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
34223435
}
34233436

34243437
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
@@ -1114,11 +1114,12 @@ protected synchronized void attachOrDetachISO(final Connect conn, final String v
11141114
storagePool = storagePoolMgr.getStoragePoolByURI(path);
11151115
}
11161116
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
1117+
final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol);
11171118
isoPath = isoVol.getPath();
11181119

1119-
iso.defISODisk(isoPath, isUefiEnabled);
1120+
iso.defISODisk(isoPath, isUefiEnabled, isoDiskType);
11201121
} else {
1121-
iso.defISODisk(null, isUefiEnabled);
1122+
iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE);
11221123
}
11231124

11241125
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
@@ -172,7 +172,7 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, S
172172
* Checks if downloaded template is extractable
173173
* @return true if it should be extracted, false if not
174174
*/
175-
private boolean isTemplateExtractable(String templatePath) {
175+
public static boolean isTemplateExtractable(String templatePath) {
176176
String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'");
177177
return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip");
178178
}
@@ -182,7 +182,7 @@ private boolean isTemplateExtractable(String templatePath) {
182182
* @param downloadedTemplateFile
183183
* @param templateUuid
184184
*/
185-
private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
185+
public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
186186
if (downloadedTemplateFile.endsWith(".zip")) {
187187
return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid;
188188
} else if (downloadedTemplateFile.endsWith(".bz2")) {
@@ -197,7 +197,7 @@ private String getExtractCommandForDownloadedFile(String downloadedTemplateFile,
197197
/**
198198
* Extract downloaded template into installPath, remove compressed file
199199
*/
200-
private void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
200+
public static void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
201201
String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile);
202202
Script.runSimpleBashScript(extractCommand);
203203
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
@@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Disable discard="unmap" for ide devices and qemu < 7.0
1313
https://bugzilla.redhat.com/show_bug.cgi?id=2029980
1414

15+
## [2024-10-14]
16+
17+
### Added
18+
19+
- Support for ISO direct download to primary storage
20+
1521
## [2024-10-04]
1622

1723
### Added

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

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323
import java.util.List;
2424
import java.util.Map;
2525
import java.util.Optional;
26+
import java.util.UUID;
2627

2728
import javax.annotation.Nonnull;
2829

2930
import com.cloud.storage.Storage;
3031
import com.cloud.utils.exception.CloudRuntimeException;
32+
import com.cloud.utils.script.Script;
33+
3134
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
3235
import org.apache.cloudstack.utils.qemu.QemuImg;
3336
import org.apache.cloudstack.utils.qemu.QemuImgException;
@@ -55,6 +58,8 @@
5558
import com.linbit.linstor.api.model.Volume;
5659
import com.linbit.linstor.api.model.VolumeDefinition;
5760

61+
import java.io.File;
62+
5863
@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor)
5964
public class LinstorStorageAdaptor implements StorageAdaptor {
6065
private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class);
@@ -562,19 +567,14 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
562567
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
563568

564569
final DevelopersApi api = getLinstorAPI(destPools);
565-
final String rscName = LinstorUtil.RSC_PREFIX + name;
566-
try {
567-
LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName());
568-
} catch (ApiException apiExc) {
569-
s_logger.error(String.format("Error setting aux properties for %s", rscName));
570-
logLinstorAnswers(apiExc.getApiCallRcList());
571-
}
570+
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
572571

573572
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
574573
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
575574
destFile.setFormat(dstDisk.getFormat());
576575
destFile.setSize(disk.getVirtualSize());
577576

577+
final String rscName = getLinstorRscName(name);
578578
boolean zeroedDevice = resourceSupportZeroBlocks(destPools, rscName);
579579

580580
try {
@@ -619,13 +619,57 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(
619619
return null;
620620
}
621621

622+
private void fileExistsOrThrow(String templateFilePath) {
623+
File sourceFile = new File(templateFilePath);
624+
if (!sourceFile.exists()) {
625+
throw new CloudRuntimeException("Direct download template file " + sourceFile +
626+
" does not exist on this host");
627+
}
628+
}
629+
630+
private String getFinalDirectDownloadPath(String templateFilePath, KVMStoragePool destPool) {
631+
String finalSourcePath = templateFilePath;
632+
if (LibvirtStorageAdaptor.isTemplateExtractable(templateFilePath)) {
633+
finalSourcePath = templateFilePath.substring(0, templateFilePath.lastIndexOf('.'));
634+
LibvirtStorageAdaptor.extractDownloadedTemplate(templateFilePath, destPool, finalSourcePath);
635+
}
636+
return finalSourcePath;
637+
}
638+
639+
private void applyAuxProps(DevelopersApi api, String csPath, String csName, String csVMName) {
640+
final String rscName = getLinstorRscName(csPath);
641+
try {
642+
LinstorUtil.applyAuxProps(api, rscName, csName, csVMName);
643+
} catch (ApiException apiExc) {
644+
s_logger.error(String.format("Error setting aux properties for %s", rscName));
645+
logLinstorAnswers(apiExc.getApiCallRcList());
646+
}
647+
}
648+
622649
@Override
623650
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
624651
KVMStoragePool destPool, Storage.ImageFormat format,
625652
int timeout)
626653
{
627-
s_logger.debug("Linstor: createTemplateFromDirectDownloadFile");
628-
return null;
654+
s_logger.debug(String.format("Linstor: createTemplateFromDirectDownloadFile: %s/%s", templateFilePath, format));
655+
fileExistsOrThrow(templateFilePath);
656+
String name = UUID.randomUUID().toString();
657+
658+
String finalSourcePath = getFinalDirectDownloadPath(templateFilePath, destPool);
659+
660+
File finalSourceFile = new File(finalSourcePath);
661+
final KVMPhysicalDisk dstDisk = destPool.createPhysicalDisk(
662+
name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, finalSourceFile.length(), null);
663+
664+
final DevelopersApi api = getLinstorAPI(destPool);
665+
applyAuxProps(api, name, finalSourceFile.getName(), null);
666+
667+
Script.runSimpleBashScript(
668+
String.format("dd if=\"%s\" of=\"%s\" bs=64k conv=nocreat,sparse oflag=direct",
669+
finalSourcePath, dstDisk.getPath()));
670+
671+
Script.runSimpleBashScript("rm " + finalSourcePath);
672+
return dstDisk;
629673
}
630674

631675
public long getCapacity(LinstorStoragePool pool) {

0 commit comments

Comments
 (0)