From 8aeaa5c50a4f5b8aaab9c2bfaf10632196a9a15b Mon Sep 17 00:00:00 2001 From: Rene Peinthor Date: Mon, 14 Oct 2024 13:32:03 +0200 Subject: [PATCH] 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. --- .../resource/LibvirtComputingResource.java | 25 ++++++++--- .../kvm/resource/LibvirtDomainXMLParser.java | 10 +++-- .../hypervisor/kvm/resource/LibvirtVMDef.java | 24 +++++------ .../kvm/storage/KVMStorageProcessor.java | 5 ++- .../kvm/storage/LibvirtStorageAdaptor.java | 6 +-- .../kvm/storage/LinstorStorageAdaptor.java | 42 ++++++++++++++++++- 6 files changed, 84 insertions(+), 28 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index fc1c30a5988f..485b5bb77079 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -2982,6 +2982,17 @@ public String getVolumePath(final Connect conn, final DiskTO volume, boolean dis return dataPath; } + public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) { + return physicalDisk != null && + physicalDisk.getPool().getType() == StoragePoolType.Linstor && + physicalDisk.getFormat() != null && + physicalDisk.getFormat()== PhysicalDiskFormat.RAW; + } + + public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) { + return useBLOCKDiskType(physicalDisk) ? DiskDef.DiskType.BLOCK : DiskDef.DiskType.FILE; + } + public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException { final Map details = vmSpec.getDetails(); final List disks = Arrays.asList(vmSpec.getDisks()); @@ -3027,7 +3038,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) { physicalDisk = getPhysicalDiskFromNfsStore(dataStoreUrl, data); } else if (primaryDataStoreTO.getPoolType().equals(StoragePoolType.SharedMountPoint) || primaryDataStoreTO.getPoolType().equals(StoragePoolType.Filesystem) || - primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool)) { + primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool) || + primaryDataStoreTO.getPoolType().equals(StoragePoolType.Linstor)) { physicalDisk = getPhysicalDiskPrimaryStore(primaryDataStoreTO, data); } } @@ -3077,8 +3089,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) { final DiskDef disk = new DiskDef(); int devId = volume.getDiskSeq().intValue(); if (volume.getType() == Volume.Type.ISO) { - - disk.defISODisk(volPath, devId, isUefiEnabled); + final DiskDef.DiskType diskType = getDiskType(physicalDisk); + disk.defISODisk(volPath, devId, isUefiEnabled, diskType); if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { disk.setBusType(DiskDef.DiskBus.SCSI); @@ -3170,7 +3182,7 @@ public int compare(final DiskTO arg0, final DiskTO arg1) { if (vmSpec.getType() != VirtualMachine.Type.User) { final DiskDef iso = new DiskDef(); - iso.defISODisk(sysvmISOPath); + iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE); if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { iso.setBusType(DiskDef.DiskBus.SCSI); } @@ -3403,11 +3415,12 @@ public synchronized String attachOrDetachISO(final Connect conn, final String vm final String name = isoPath.substring(index + 1); final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path); final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); + final DiskDef.DiskType diskType = getDiskType(isoVol); isoPath = isoVol.getPath(); - iso.defISODisk(isoPath, diskSeq); + iso.defISODisk(isoPath, diskSeq, diskType); } else { - iso.defISODisk(null, diskSeq); + iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE); } final String result = attachOrDetachDevice(conn, true, vmName, iso.toString()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index a0dd270f999b..4364426532f6 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -126,11 +126,15 @@ public boolean parseDomainXML(String domXML) { } def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt); } else if (device.equalsIgnoreCase("cdrom")) { - def.defISODisk(diskFile, i+1, diskLabel); + def.defISODisk(diskFile, i+1, diskLabel, DiskDef.DiskType.FILE); } } else if (type.equalsIgnoreCase("block")) { - def.defBlockBasedDisk(diskDev, diskLabel, - DiskDef.DiskBus.valueOf(bus.toUpperCase())); + if (device.equalsIgnoreCase("disk")) { + def.defBlockBasedDisk(diskDev, diskLabel, + DiskDef.DiskBus.valueOf(bus.toUpperCase())); + } else if (device.equalsIgnoreCase("cdrom")) { + def.defISODisk(diskFile, i+1, diskLabel, DiskDef.DiskType.BLOCK); + } } if (StringUtils.isNotBlank(diskCacheMode)) { def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase())); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index cfd72c28b5af..ec9409420826 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -833,8 +833,8 @@ public void defFileBasedDisk(String filePath, int devId, DiskFmtType diskFmtType } } - public void defISODisk(String volPath) { - _diskType = DiskType.FILE; + public void defISODisk(String volPath, DiskType diskType) { + _diskType = diskType; _deviceType = DeviceType.CDROM; _sourcePath = volPath; _diskLabel = getDevLabel(3, DiskBus.IDE, true); @@ -843,8 +843,8 @@ public void defISODisk(String volPath) { _bus = DiskBus.IDE; } - public void defISODisk(String volPath, boolean isUefiEnabled) { - _diskType = DiskType.FILE; + public void defISODisk(String volPath, boolean isUefiEnabled, DiskType diskType) { + _diskType = diskType; _deviceType = DeviceType.CDROM; _sourcePath = volPath; _bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE; @@ -853,18 +853,18 @@ public void defISODisk(String volPath, boolean isUefiEnabled) { _diskCacheMode = DiskCacheMode.NONE; } - public void defISODisk(String volPath, Integer devId) { - defISODisk(volPath, devId, null); + public void defISODisk(String volPath, Integer devId, DiskType diskType) { + defISODisk(volPath, devId, null, diskType); } - public void defISODisk(String volPath, Integer devId, String diskLabel) { + public void defISODisk(String volPath, Integer devId, String diskLabel, DiskType diskType) { if (devId == null && StringUtils.isBlank(diskLabel)) { s_logger.debug(String.format("No ID or label informed for volume [%s].", volPath)); - defISODisk(volPath); + defISODisk(volPath, diskType); return; } - _diskType = DiskType.FILE; + _diskType = diskType; _deviceType = DeviceType.CDROM; _sourcePath = volPath; @@ -881,11 +881,11 @@ public void defISODisk(String volPath, Integer devId, String diskLabel) { _bus = DiskBus.IDE; } - public void defISODisk(String volPath, Integer devId,boolean isSecure) { + public void defISODisk(String volPath, Integer devId, boolean isSecure, DiskType diskType) { if (!isSecure) { - defISODisk(volPath, devId); + defISODisk(volPath, devId, diskType); } else { - _diskType = DiskType.FILE; + _diskType = diskType; _deviceType = DeviceType.CDROM; _sourcePath = volPath; _diskLabel = getDevLabel(devId, DiskBus.SATA, true); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index e3ee131a84b2..c42aed5dc9d6 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -1112,11 +1112,12 @@ protected synchronized void attachOrDetachISO(final Connect conn, final String v storagePool = storagePoolMgr.getStoragePoolByURI(path); } final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name); + final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol); isoPath = isoVol.getPath(); - iso.defISODisk(isoPath, isUefiEnabled); + iso.defISODisk(isoPath, isUefiEnabled, isoDiskType); } else { - iso.defISODisk(null, isUefiEnabled); + iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE); } final List disks = resource.getDisks(conn, vmName); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index b5895f65d4b2..98a70a0865f1 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -171,7 +171,7 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, S * Checks if downloaded template is extractable * @return true if it should be extracted, false if not */ - private boolean isTemplateExtractable(String templatePath) { + public static boolean isTemplateExtractable(String templatePath) { String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); } @@ -181,7 +181,7 @@ private boolean isTemplateExtractable(String templatePath) { * @param downloadedTemplateFile * @param templateUuid */ - private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) { + public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) { if (downloadedTemplateFile.endsWith(".zip")) { return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid; } else if (downloadedTemplateFile.endsWith(".bz2")) { @@ -196,7 +196,7 @@ private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, /** * Extract downloaded template into installPath, remove compressed file */ - private void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) { + public static void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) { String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile); Script.runSimpleBashScript(extractCommand); Script.runSimpleBashScript("rm -f " + downloadedTemplateFile); diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 9c4e41a489d7..4c4717b56abc 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -22,11 +22,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import javax.annotation.Nonnull; import com.cloud.storage.Storage; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + import org.apache.cloudstack.storage.datastore.util.LinstorUtil; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImgException; @@ -53,6 +56,8 @@ import com.linbit.linstor.api.model.Volume; import com.linbit.linstor.api.model.VolumeDefinition; +import java.io.File; + @StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor) public class LinstorStorageAdaptor implements StorageAdaptor { private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class); @@ -578,8 +583,41 @@ public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFileP KVMStoragePool destPool, Storage.ImageFormat format, int timeout) { - s_logger.debug("Linstor: createTemplateFromDirectDownloadFile"); - return null; + s_logger.debug(String.format("Linstor: createTemplateFromDirectDownloadFile: %s/%s", templateFilePath, format)); + { + File sourceFile = new File(templateFilePath); + if (!sourceFile.exists()) { + throw new CloudRuntimeException("Direct download template file " + sourceFile + + " does not exist on this host"); + } + } + String name = UUID.randomUUID().toString(); + + String finalSourcePath = templateFilePath; + if (LibvirtStorageAdaptor.isTemplateExtractable(templateFilePath)) { + finalSourcePath = templateFilePath.substring(0, templateFilePath.lastIndexOf('.')); + LibvirtStorageAdaptor.extractDownloadedTemplate(templateFilePath, destPool, finalSourcePath); + } + + File finalSourceFile = new File(finalSourcePath); + final KVMPhysicalDisk dstDisk = destPool.createPhysicalDisk( + name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, finalSourceFile.length(), null); + + final DevelopersApi api = getLinstorAPI(destPool); + final String rscName = getLinstorRscName(name); + try { + LinstorUtil.applyAuxProps(api, rscName, finalSourceFile.getName(), null); + } catch (ApiException apiExc) { + s_logger.error(String.format("Error setting aux properties for %s", rscName)); + logLinstorAnswers(apiExc.getApiCallRcList()); + } + + Script.runSimpleBashScript( + String.format("dd if=\"%s\" of=\"%s\" bs=64k conv=nocreat,sparse oflag=direct", + finalSourcePath, dstDisk.getPath())); + + Script.runSimpleBashScript("rm " + finalSourcePath); + return dstDisk; } public long getCapacity(LinstorStoragePool pool) {