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;
}
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<String, String> details = vmSpec.getDetails();
final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
@ -3028,7 +3039,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
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);
}
}
@ -3078,8 +3090,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
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);
@ -3171,7 +3183,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
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);
}
@ -3384,7 +3396,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
List<DiskDef> disks = getDisks(conn, vmName);
DiskDef configdrive = null;
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;
}
}
@ -3414,11 +3426,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
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());
@ -3426,7 +3439,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final List<DiskDef> disks = getDisks(conn, vmName);
for (final DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM
&& (diskSeq == null || disk.getDiskLabel() == iso.getDiskLabel())) {
&& (diskSeq == null || disk.getDiskLabel().equals(iso.getDiskLabel()))) {
cleanupDisk(disk);
}
}
@ -4002,7 +4015,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return stopVMInternal(conn, vmName, true);
}
String ret = stopVMInternal(conn, vmName, false);
if (ret == Script.ERR_TIMEOUT) {
if (Script.ERR_TIMEOUT.equals(ret)) {
ret = stopVMInternal(conn, vmName, true);
} else if (ret != null) {
/*

View File

@ -126,11 +126,10 @@ public class LibvirtDomainXMLParser {
}
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()));
parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
}
if (StringUtils.isNotBlank(diskCacheMode)) {
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
@ -449,6 +448,25 @@ public class LibvirtDomainXMLParser {
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() {
return vncPort;
}

View File

@ -833,8 +833,8 @@ public class LibvirtVMDef {
}
}
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 class LibvirtVMDef {
_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 class LibvirtVMDef {
_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 class LibvirtVMDef {
_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);

View File

@ -1114,11 +1114,12 @@ public class KVMStorageProcessor implements StorageProcessor {
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<DiskDef> disks = resource.getDisks(conn, vmName);

View File

@ -172,7 +172,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* 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");
}
@ -182,7 +182,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* @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")) {
@ -197,7 +197,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
/**
* 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);

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
https://bugzilla.redhat.com/show_bug.cgi?id=2029980
## [2024-10-14]
### Added
- Support for ISO direct download to primary storage
## [2024-10-04]
### Added

View File

@ -23,11 +23,13 @@ import java.util.HashMap;
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;
@ -56,6 +58,8 @@ import com.linbit.linstor.api.model.StoragePool;
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);
@ -563,13 +567,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
final DevelopersApi api = getLinstorAPI(destPools);
final String rscName = LinstorUtil.RSC_PREFIX + name;
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());
}
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
@ -620,13 +618,57 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
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
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
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));
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) {