Linstor: add support for ISO block devices and direct download (#9792)

This commit is contained in:
Rene Peinthor 2024-11-28 17:47:47 +01:00 committed by GitHub
parent a73841a693
commit d54b105a03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 118 additions and 38 deletions

View File

@ -2983,6 +2983,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return dataPath; 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 { public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException {
final Map<String, String> details = vmSpec.getDetails(); final Map<String, String> details = vmSpec.getDetails();
final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks()); final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
@ -3028,7 +3039,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
physicalDisk = getPhysicalDiskFromNfsStore(dataStoreUrl, data); physicalDisk = getPhysicalDiskFromNfsStore(dataStoreUrl, data);
} else if (primaryDataStoreTO.getPoolType().equals(StoragePoolType.SharedMountPoint) || } else if (primaryDataStoreTO.getPoolType().equals(StoragePoolType.SharedMountPoint) ||
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Filesystem) || primaryDataStoreTO.getPoolType().equals(StoragePoolType.Filesystem) ||
primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool)) { primaryDataStoreTO.getPoolType().equals(StoragePoolType.StorPool) ||
primaryDataStoreTO.getPoolType().equals(StoragePoolType.Linstor)) {
physicalDisk = getPhysicalDiskPrimaryStore(primaryDataStoreTO, data); physicalDisk = getPhysicalDiskPrimaryStore(primaryDataStoreTO, data);
} }
} }
@ -3078,8 +3090,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final DiskDef disk = new DiskDef(); final DiskDef disk = new DiskDef();
int devId = volume.getDiskSeq().intValue(); int devId = volume.getDiskSeq().intValue();
if (volume.getType() == Volume.Type.ISO) { if (volume.getType() == Volume.Type.ISO) {
final DiskDef.DiskType diskType = getDiskType(physicalDisk);
disk.defISODisk(volPath, devId, isUefiEnabled); disk.defISODisk(volPath, devId, isUefiEnabled, diskType);
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
disk.setBusType(DiskDef.DiskBus.SCSI); disk.setBusType(DiskDef.DiskBus.SCSI);
@ -3171,7 +3183,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
if (vmSpec.getType() != VirtualMachine.Type.User) { if (vmSpec.getType() != VirtualMachine.Type.User) {
final DiskDef iso = new DiskDef(); final DiskDef iso = new DiskDef();
iso.defISODisk(sysvmISOPath); iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE);
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { if (guestCpuArch != null && guestCpuArch.equals("aarch64")) {
iso.setBusType(DiskDef.DiskBus.SCSI); iso.setBusType(DiskDef.DiskBus.SCSI);
} }
@ -3384,7 +3396,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
List<DiskDef> disks = getDisks(conn, vmName); List<DiskDef> disks = getDisks(conn, vmName);
DiskDef configdrive = null; DiskDef configdrive = null;
for (DiskDef disk : disks) { for (DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && disk.getDiskLabel() == CONFIG_DRIVE_ISO_DISK_LABEL) { if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && CONFIG_DRIVE_ISO_DISK_LABEL.equals(disk.getDiskLabel())) {
configdrive = disk; configdrive = disk;
} }
} }
@ -3414,11 +3426,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final String name = isoPath.substring(index + 1); final String name = isoPath.substring(index + 1);
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path); final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path);
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
final DiskDef.DiskType diskType = getDiskType(isoVol);
isoPath = isoVol.getPath(); isoPath = isoVol.getPath();
iso.defISODisk(isoPath, diskSeq); iso.defISODisk(isoPath, diskSeq, diskType);
} else { } else {
iso.defISODisk(null, diskSeq); iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
} }
final String result = attachOrDetachDevice(conn, true, vmName, iso.toString()); final String result = attachOrDetachDevice(conn, true, vmName, iso.toString());
@ -3426,7 +3439,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final List<DiskDef> disks = getDisks(conn, vmName); final List<DiskDef> disks = getDisks(conn, vmName);
for (final DiskDef disk : disks) { for (final DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM if (disk.getDeviceType() == DiskDef.DeviceType.CDROM
&& (diskSeq == null || disk.getDiskLabel() == iso.getDiskLabel())) { && (diskSeq == null || disk.getDiskLabel().equals(iso.getDiskLabel()))) {
cleanupDisk(disk); cleanupDisk(disk);
} }
} }
@ -4002,7 +4015,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return stopVMInternal(conn, vmName, true); return stopVMInternal(conn, vmName, true);
} }
String ret = stopVMInternal(conn, vmName, false); String ret = stopVMInternal(conn, vmName, false);
if (ret == Script.ERR_TIMEOUT) { if (Script.ERR_TIMEOUT.equals(ret)) {
ret = stopVMInternal(conn, vmName, true); ret = stopVMInternal(conn, vmName, true);
} else if (ret != null) { } else if (ret != null) {
/* /*

View File

@ -126,11 +126,10 @@ public class LibvirtDomainXMLParser {
} }
def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt); def.defFileBasedDisk(diskFile, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()), fmt);
} else if (device.equalsIgnoreCase("cdrom")) { } 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")) { } else if (type.equalsIgnoreCase("block")) {
def.defBlockBasedDisk(diskDev, diskLabel, parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
DiskDef.DiskBus.valueOf(bus.toUpperCase()));
} }
if (StringUtils.isNotBlank(diskCacheMode)) { if (StringUtils.isNotBlank(diskCacheMode)) {
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase())); def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
@ -449,6 +448,25 @@ public class LibvirtDomainXMLParser {
return node.getAttribute(attr); return node.getAttribute(attr);
} }
/**
* Parse the disk block part of the libvirt XML.
* @param def
* @param device
* @param diskDev
* @param diskLabel
* @param bus
* @param diskFile
* @param curDiskIndex
*/
private void parseDiskBlock(DiskDef def, String device, String diskDev, String diskLabel, String bus,
String diskFile, int curDiskIndex) {
if (device.equalsIgnoreCase("disk")) {
def.defBlockBasedDisk(diskDev, diskLabel, DiskDef.DiskBus.valueOf(bus.toUpperCase()));
} else if (device.equalsIgnoreCase("cdrom")) {
def.defISODisk(diskFile, curDiskIndex+1, diskLabel, DiskDef.DiskType.BLOCK);
}
}
public Integer getVncPort() { public Integer getVncPort() {
return vncPort; return vncPort;
} }

View File

@ -833,8 +833,8 @@ public class LibvirtVMDef {
} }
} }
public void defISODisk(String volPath) { public void defISODisk(String volPath, DiskType diskType) {
_diskType = DiskType.FILE; _diskType = diskType;
_deviceType = DeviceType.CDROM; _deviceType = DeviceType.CDROM;
_sourcePath = volPath; _sourcePath = volPath;
_diskLabel = getDevLabel(3, DiskBus.IDE, true); _diskLabel = getDevLabel(3, DiskBus.IDE, true);
@ -843,8 +843,8 @@ public class LibvirtVMDef {
_bus = DiskBus.IDE; _bus = DiskBus.IDE;
} }
public void defISODisk(String volPath, boolean isUefiEnabled) { public void defISODisk(String volPath, boolean isUefiEnabled, DiskType diskType) {
_diskType = DiskType.FILE; _diskType = diskType;
_deviceType = DeviceType.CDROM; _deviceType = DeviceType.CDROM;
_sourcePath = volPath; _sourcePath = volPath;
_bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE; _bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE;
@ -853,18 +853,18 @@ public class LibvirtVMDef {
_diskCacheMode = DiskCacheMode.NONE; _diskCacheMode = DiskCacheMode.NONE;
} }
public void defISODisk(String volPath, Integer devId) { public void defISODisk(String volPath, Integer devId, DiskType diskType) {
defISODisk(volPath, devId, null); 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)) { if (devId == null && StringUtils.isBlank(diskLabel)) {
s_logger.debug(String.format("No ID or label informed for volume [%s].", volPath)); s_logger.debug(String.format("No ID or label informed for volume [%s].", volPath));
defISODisk(volPath); defISODisk(volPath, diskType);
return; return;
} }
_diskType = DiskType.FILE; _diskType = diskType;
_deviceType = DeviceType.CDROM; _deviceType = DeviceType.CDROM;
_sourcePath = volPath; _sourcePath = volPath;
@ -881,11 +881,11 @@ public class LibvirtVMDef {
_bus = DiskBus.IDE; _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) { if (!isSecure) {
defISODisk(volPath, devId); defISODisk(volPath, devId, diskType);
} else { } else {
_diskType = DiskType.FILE; _diskType = diskType;
_deviceType = DeviceType.CDROM; _deviceType = DeviceType.CDROM;
_sourcePath = volPath; _sourcePath = volPath;
_diskLabel = getDevLabel(devId, DiskBus.SATA, true); _diskLabel = getDevLabel(devId, DiskBus.SATA, true);

View File

@ -1114,11 +1114,12 @@ public class KVMStorageProcessor implements StorageProcessor {
storagePool = storagePoolMgr.getStoragePoolByURI(path); storagePool = storagePoolMgr.getStoragePoolByURI(path);
} }
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name); final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
final DiskDef.DiskType isoDiskType = LibvirtComputingResource.getDiskType(isoVol);
isoPath = isoVol.getPath(); isoPath = isoVol.getPath();
iso.defISODisk(isoPath, isUefiEnabled); iso.defISODisk(isoPath, isUefiEnabled, isoDiskType);
} else { } else {
iso.defISODisk(null, isUefiEnabled); iso.defISODisk(null, isUefiEnabled, DiskDef.DiskType.FILE);
} }
final List<DiskDef> disks = resource.getDisks(conn, vmName); final List<DiskDef> disks = resource.getDisks(conn, vmName);

View File

@ -172,7 +172,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* Checks if downloaded template is extractable * Checks if downloaded template is extractable
* @return true if it should be extracted, false if not * @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}'"); String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'");
return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip");
} }
@ -182,7 +182,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* @param downloadedTemplateFile * @param downloadedTemplateFile
* @param templateUuid * @param templateUuid
*/ */
private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) { public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) {
if (downloadedTemplateFile.endsWith(".zip")) { if (downloadedTemplateFile.endsWith(".zip")) {
return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid; return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid;
} else if (downloadedTemplateFile.endsWith(".bz2")) { } else if (downloadedTemplateFile.endsWith(".bz2")) {
@ -197,7 +197,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
/** /**
* Extract downloaded template into installPath, remove compressed file * 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); String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile);
Script.runSimpleBashScript(extractCommand); Script.runSimpleBashScript(extractCommand);
Script.runSimpleBashScript("rm -f " + downloadedTemplateFile); Script.runSimpleBashScript("rm -f " + downloadedTemplateFile);

View File

@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Disable discard="unmap" for ide devices and qemu < 7.0 - Disable discard="unmap" for ide devices and qemu < 7.0
https://bugzilla.redhat.com/show_bug.cgi?id=2029980 https://bugzilla.redhat.com/show_bug.cgi?id=2029980
## [2024-10-14]
### Added
- Support for ISO direct download to primary storage
## [2024-10-04] ## [2024-10-04]
### Added ### Added

View File

@ -23,11 +23,13 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import org.apache.cloudstack.storage.datastore.util.LinstorUtil; import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg;
@ -56,6 +58,8 @@ import com.linbit.linstor.api.model.StoragePool;
import com.linbit.linstor.api.model.Volume; import com.linbit.linstor.api.model.Volume;
import com.linbit.linstor.api.model.VolumeDefinition; import com.linbit.linstor.api.model.VolumeDefinition;
import java.io.File;
@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor) @StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor)
public class LinstorStorageAdaptor implements StorageAdaptor { public class LinstorStorageAdaptor implements StorageAdaptor {
private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class); private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class);
@ -563,13 +567,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null); name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
final DevelopersApi api = getLinstorAPI(destPools); final DevelopersApi api = getLinstorAPI(destPools);
final String rscName = LinstorUtil.RSC_PREFIX + name; applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
try {
LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName());
} catch (ApiException apiExc) {
s_logger.error(String.format("Error setting aux properties for %s", rscName));
logLinstorAnswers(apiExc.getApiCallRcList());
}
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath())); s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath()); final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@ -620,13 +618,57 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
return null; return null;
} }
private void fileExistsOrThrow(String templateFilePath) {
File sourceFile = new File(templateFilePath);
if (!sourceFile.exists()) {
throw new CloudRuntimeException("Direct download template file " + sourceFile +
" does not exist on this host");
}
}
private String getFinalDirectDownloadPath(String templateFilePath, KVMStoragePool destPool) {
String finalSourcePath = templateFilePath;
if (LibvirtStorageAdaptor.isTemplateExtractable(templateFilePath)) {
finalSourcePath = templateFilePath.substring(0, templateFilePath.lastIndexOf('.'));
LibvirtStorageAdaptor.extractDownloadedTemplate(templateFilePath, destPool, finalSourcePath);
}
return finalSourcePath;
}
private void applyAuxProps(DevelopersApi api, String csPath, String csName, String csVMName) {
final String rscName = getLinstorRscName(csPath);
try {
LinstorUtil.applyAuxProps(api, rscName, csName, csVMName);
} catch (ApiException apiExc) {
s_logger.error(String.format("Error setting aux properties for %s", rscName));
logLinstorAnswers(apiExc.getApiCallRcList());
}
}
@Override @Override
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
KVMStoragePool destPool, Storage.ImageFormat format, KVMStoragePool destPool, Storage.ImageFormat format,
int timeout) int timeout)
{ {
s_logger.debug("Linstor: createTemplateFromDirectDownloadFile"); s_logger.debug(String.format("Linstor: createTemplateFromDirectDownloadFile: %s/%s", templateFilePath, format));
return null; fileExistsOrThrow(templateFilePath);
String name = UUID.randomUUID().toString();
String finalSourcePath = getFinalDirectDownloadPath(templateFilePath, destPool);
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);
applyAuxProps(api, name, finalSourceFile.getName(), null);
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) { public long getCapacity(LinstorStoragePool pool) {