Skip to content

Commit aeb84b0

Browse files
committed
Linstor: add support for ISO block devices and direct download (apache#9792)
1 parent 2fe3fce commit aeb84b0

File tree

7 files changed

+117
-37
lines changed

7 files changed

+117
-37
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
@@ -3000,6 +3000,17 @@ public String getVolumePath(final Connect conn, final DiskTO volume, boolean dis
30003000
return dataPath;
30013001
}
30023002

3003+
public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) {
3004+
return physicalDisk != null &&
3005+
physicalDisk.getPool().getType() == StoragePoolType.Linstor &&
3006+
physicalDisk.getFormat() != null &&
3007+
physicalDisk.getFormat()== PhysicalDiskFormat.RAW;
3008+
}
3009+
3010+
public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) {
3011+
return useBLOCKDiskType(physicalDisk) ? DiskDef.DiskType.BLOCK : DiskDef.DiskType.FILE;
3012+
}
3013+
30033014
public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException {
30043015
final Map<String, String> details = vmSpec.getDetails();
30053016
final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
@@ -3045,7 +3056,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
30453056
physicalDisk = getPhysicalDiskFromNfsStore(dataStoreUrl, data);
30463057
} else if (primaryDataStoreTO.getPoolType().equals(StoragePoolType.SharedMountPoint) ||
30473058
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Filesystem) ||
3048-
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool)) {
3059+
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool) ||
3060+
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Linstor)) {
30493061
physicalDisk = getPhysicalDiskPrimaryStore(primaryDataStoreTO, data);
30503062
}
30513063
}
@@ -3095,8 +3107,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
30953107
final DiskDef disk = new DiskDef();
30963108
int devId = volume.getDiskSeq().intValue();
30973109
if (volume.getType() == Volume.Type.ISO) {
3098-
3099-
disk.defISODisk(volPath, devId, isUefiEnabled);
3110+
final DiskDef.DiskType diskType = getDiskType(physicalDisk);
3111+
disk.defISODisk(volPath, devId, isUefiEnabled, diskType);
31003112

31013113
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
31023114
disk.setBusType(DiskDef.DiskBus.SCSI);
@@ -3195,7 +3207,7 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
31953207

31963208
if (vmSpec.getType() != VirtualMachine.Type.User) {
31973209
final DiskDef iso = new DiskDef();
3198-
iso.defISODisk(sysvmISOPath);
3210+
iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE);
31993211
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
32003212
iso.setBusType(DiskDef.DiskBus.SCSI);
32013213
}
@@ -3408,7 +3420,7 @@ public void detachAndAttachConfigDriveISO(final Connect conn, final String vmNam
34083420
List<DiskDef> disks = getDisks(conn, vmName);
34093421
DiskDef configdrive = null;
34103422
for (DiskDef disk : disks) {
3411-
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && disk.getDiskLabel() == CONFIG_DRIVE_ISO_DISK_LABEL) {
3423+
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && CONFIG_DRIVE_ISO_DISK_LABEL.equals(disk.getDiskLabel())) {
34123424
configdrive = disk;
34133425
}
34143426
}
@@ -3438,19 +3450,20 @@ public synchronized String attachOrDetachISO(final Connect conn, final String vm
34383450
final String name = isoPath.substring(index + 1);
34393451
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path);
34403452
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
3453+
final DiskDef.DiskType diskType = getDiskType(isoVol);
34413454
isoPath = isoVol.getPath();
34423455

3443-
iso.defISODisk(isoPath, diskSeq);
3456+
iso.defISODisk(isoPath, diskSeq, diskType);
34443457
} else {
3445-
iso.defISODisk(null, diskSeq);
3458+
iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
34463459
}
34473460

34483461
final String result = attachOrDetachDevice(conn, true, vmName, iso.toString());
34493462
if (result == null && !isAttach) {
34503463
final List<DiskDef> disks = getDisks(conn, vmName);
34513464
for (final DiskDef disk : disks) {
34523465
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM
3453-
&& (diskSeq == null || disk.getDiskLabel() == iso.getDiskLabel())) {
3466+
&& (diskSeq == null || disk.getDiskLabel().equals(iso.getDiskLabel()))) {
34543467
cleanupDisk(disk);
34553468
}
34563469
}
@@ -4046,7 +4059,7 @@ public String stopVM(final Connect conn, final String vmName, final boolean forc
40464059
return stopVMInternal(conn, vmName, true);
40474060
}
40484061
String ret = stopVMInternal(conn, vmName, false);
4049-
if (ret == Script.ERR_TIMEOUT) {
4062+
if (Script.ERR_TIMEOUT.equals(ret)) {
40504063
ret = stopVMInternal(conn, vmName, true);
40514064
} else if (ret != null) {
40524065
/*

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
@@ -127,11 +127,10 @@ public boolean parseDomainXML(String domXML) {
127127
}
128128
def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
129129
} else if (device.equalsIgnoreCase("cdrom")) {
130-
def.defISODisk(diskFile, i+1, diskLabel);
130+
def.defISODisk(diskFile, i+1, diskLabel, DiskDef.DiskType.FILE);
131131
}
132132
} else if (type.equalsIgnoreCase("block")) {
133-
def.defBlockBasedDisk(diskDev, diskLabel,
134-
DiskDef.DiskBus.valueOf(bus.toUpperCase()));
133+
parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
135134
}
136135
if (StringUtils.isNotBlank(diskCacheMode)) {
137136
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
@@ -450,6 +449,25 @@ private static String getAttrValue(String tag, String attr, Element eElement) {
450449
return node.getAttribute(attr);
451450
}
452451

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

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
@@ -894,8 +894,8 @@ public void defFileBasedDisk(String filePath, int devId, DiskFmtType diskFmtType
894894
}
895895
}
896896

897-
public void defISODisk(String volPath) {
898-
_diskType = DiskType.FILE;
897+
public void defISODisk(String volPath, DiskType diskType) {
898+
_diskType = diskType;
899899
_deviceType = DeviceType.CDROM;
900900
_sourcePath = volPath;
901901
_diskLabel = getDevLabel(3, DiskBus.IDE, true);
@@ -904,8 +904,8 @@ public void defISODisk(String volPath) {
904904
_bus = DiskBus.IDE;
905905
}
906906

907-
public void defISODisk(String volPath, boolean isUefiEnabled) {
908-
_diskType = DiskType.FILE;
907+
public void defISODisk(String volPath, boolean isUefiEnabled, DiskType diskType) {
908+
_diskType = diskType;
909909
_deviceType = DeviceType.CDROM;
910910
_sourcePath = volPath;
911911
_bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE;
@@ -914,18 +914,18 @@ public void defISODisk(String volPath, boolean isUefiEnabled) {
914914
_diskCacheMode = DiskCacheMode.NONE;
915915
}
916916

917-
public void defISODisk(String volPath, Integer devId) {
918-
defISODisk(volPath, devId, null);
917+
public void defISODisk(String volPath, Integer devId, DiskType diskType) {
918+
defISODisk(volPath, devId, null, diskType);
919919
}
920920

921-
public void defISODisk(String volPath, Integer devId, String diskLabel) {
921+
public void defISODisk(String volPath, Integer devId, String diskLabel, DiskType diskType) {
922922
if (devId == null && StringUtils.isBlank(diskLabel)) {
923923
LOGGER.debug(String.format("No ID or label informed for volume [%s].", volPath));
924-
defISODisk(volPath);
924+
defISODisk(volPath, diskType);
925925
return;
926926
}
927927

928-
_diskType = DiskType.FILE;
928+
_diskType = diskType;
929929
_deviceType = DeviceType.CDROM;
930930
_sourcePath = volPath;
931931

@@ -942,11 +942,11 @@ public void defISODisk(String volPath, Integer devId, String diskLabel) {
942942
_bus = DiskBus.IDE;
943943
}
944944

945-
public void defISODisk(String volPath, Integer devId,boolean isSecure) {
945+
public void defISODisk(String volPath, Integer devId, boolean isSecure, DiskType diskType) {
946946
if (!isSecure) {
947-
defISODisk(volPath, devId);
947+
defISODisk(volPath, devId, diskType);
948948
} else {
949-
_diskType = DiskType.FILE;
949+
_diskType = diskType;
950950
_deviceType = DeviceType.CDROM;
951951
_sourcePath = volPath;
952952
_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
@@ -1119,11 +1119,12 @@ protected synchronized void attachOrDetachISO(final Connect conn, final String v
11191119
storagePool = storagePoolMgr.getStoragePoolByURI(path);
11201120
}
11211121
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
1122+
final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol);
11221123
isoPath = isoVol.getPath();
11231124

1124-
iso.defISODisk(isoPath, isUefiEnabled);
1125+
iso.defISODisk(isoPath, isUefiEnabled, isoDiskType);
11251126
} else {
1126-
iso.defISODisk(null, isUefiEnabled);
1127+
iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE);
11271128
}
11281129

11291130
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
@@ -173,7 +173,7 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, S
173173
* Checks if downloaded template is extractable
174174
* @return true if it should be extracted, false if not
175175
*/
176-
private boolean isTemplateExtractable(String templatePath) {
176+
public static boolean isTemplateExtractable(String templatePath) {
177177
String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'");
178178
return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip");
179179
}
@@ -183,7 +183,7 @@ private boolean isTemplateExtractable(String templatePath) {
183183
* @param downloadedTemplateFile
184184
* @param templateUuid
185185
*/
186-
private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
186+
public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
187187
if (downloadedTemplateFile.endsWith(".zip")) {
188188
return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid;
189189
} else if (downloadedTemplateFile.endsWith(".bz2")) {
@@ -198,7 +198,7 @@ private String getExtractCommandForDownloadedFile(String downloadedTemplateFile,
198198
/**
199199
* Extract downloaded template into installPath, remove compressed file
200200
*/
201-
private void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
201+
public static void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) {
202202
String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile);
203203
Script.runSimpleBashScript(extractCommand);
204204
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: 50 additions & 8 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;
@@ -57,6 +59,8 @@
5759
import com.linbit.linstor.api.model.Volume;
5860
import com.linbit.linstor.api.model.VolumeDefinition;
5961

62+
import java.io.File;
63+
6064
public class LinstorStorageAdaptor implements StorageAdaptor {
6165
protected Logger logger = LogManager.getLogger(getClass());
6266
private static final Map<String, KVMStoragePool> MapStorageUuidToStoragePool = new HashMap<>();
@@ -566,13 +570,7 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
566570
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
567571

568572
final DevelopersApi api = getLinstorAPI(destPools);
569-
final String rscName = LinstorUtil.RSC_PREFIX + name;
570-
try {
571-
LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName());
572-
} catch (ApiException apiExc) {
573-
logger.error("Error setting aux properties for {}", rscName);
574-
logLinstorAnswers(apiExc.getApiCallRcList());
575-
}
573+
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
576574

577575
logger.debug("Linstor.copyPhysicalDisk: dstPath: {}", dstDisk.getPath());
578576
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@@ -623,13 +621,57 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(
623621
return null;
624622
}
625623

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

635677
public long getCapacity(LinstorStoragePool pool) {

0 commit comments

Comments
 (0)