Skip to content

Commit

Permalink
linstor/kvm: add support for ISO block devices and direct download
Browse files Browse the repository at this point in the history
If Linstor storage pool is used, use the BLOCK qemu driver for the
Linstor block device.
  • Loading branch information
rp- committed Nov 4, 2024
1 parent 352909f commit d17325d
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> details = vmSpec.getDetails();
final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,10 @@ 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()));
parseDiskBlock(def, device, diskDev, diskLabel, bus, diskFile, i);
}
if (StringUtils.isNotBlank(diskCacheMode)) {
def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase()));
Expand Down Expand Up @@ -449,6 +448,25 @@ private static String getAttrValue(String tag, String attr, Element eElement) {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<DiskDef> disks = resource.getDisks(conn, vmName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,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");
}
Expand All @@ -180,7 +180,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")) {
Expand All @@ -195,7 +195,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);
Expand Down
6 changes: 6 additions & 0 deletions plugins/storage/volume/linstor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to Linstor CloudStack plugin will be documented in this file
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2024-10-14]

### Added

- Support for ISO direct download to primary storage

## [2024-10-04]

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
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;
Expand Down Expand Up @@ -54,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);
Expand Down Expand Up @@ -517,13 +521,7 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
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());
Expand Down Expand Up @@ -574,13 +572,57 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(
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) {
Expand Down

0 comments on commit d17325d

Please sign in to comment.