Skip to content

Commit d54b105

Browse files
authored
Linstor: add support for ISO block devices and direct download (#9792)
1 parent a73841a commit d54b105

File tree

7 files changed

+118
-38
lines changed

7 files changed

+118
-38
lines changed

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

Lines changed: 22 additions & 9 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
}
@@ -3384,7 +3396,7 @@ public void detachAndAttachConfigDriveISO(final Connect conn, final String vmNam
33843396
List<DiskDef> disks = getDisks(conn, vmName);
33853397
DiskDef configdrive = null;
33863398
for (DiskDef disk : disks) {
3387-
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && disk.getDiskLabel() == CONFIG_DRIVE_ISO_DISK_LABEL) {
3399+
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && CONFIG_DRIVE_ISO_DISK_LABEL.equals(disk.getDiskLabel())) {
33883400
configdrive = disk;
33893401
}
33903402
}
@@ -3414,19 +3426,20 @@ 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());
34253438
if (result == null && !isAttach) {
34263439
final List<DiskDef> disks = getDisks(conn, vmName);
34273440
for (final DiskDef disk : disks) {
34283441
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM
3429-
&& (diskSeq == null || disk.getDiskLabel() == iso.getDiskLabel())) {
3442+
&& (diskSeq == null || disk.getDiskLabel().equals(iso.getDiskLabel()))) {
34303443
cleanupDisk(disk);
34313444
}
34323445
}
@@ -4002,7 +4015,7 @@ public String stopVM(final Connect conn, final String vmName, final boolean forc
40024015
return stopVMInternal(conn, vmName, true);
40034016
}
40044017
String ret = stopVMInternal(conn, vmName, false);
4005-
if (ret == Script.ERR_TIMEOUT) {
4018+
if (Script.ERR_TIMEOUT.equals(ret)) {
40064019
ret = stopVMInternal(conn, vmName, true);
40074020
} else if (ret != null) {
40084021
/*

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: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
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;
3133

3234
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
3335
import org.apache.cloudstack.utils.qemu.QemuImg;
@@ -56,6 +58,8 @@
5658
import com.linbit.linstor.api.model.Volume;
5759
import com.linbit.linstor.api.model.VolumeDefinition;
5860

61+
import java.io.File;
62+
5963
@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor)
6064
public class LinstorStorageAdaptor implements StorageAdaptor {
6165
private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class);
@@ -563,13 +567,7 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
563567
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
564568

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

574572
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
575573
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@@ -620,13 +618,57 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(
620618
return null;
621619
}
622620

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

632674
public long getCapacity(LinstorStoragePool pool) {

0 commit comments

Comments
 (0)