mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Validate qcow2 file during import operation (#11264)
This commit is contained in:
		
							parent
							
								
									264e404108
								
							
						
					
					
						commit
						75a2b3cc54
					
				| @ -17,22 +17,33 @@ | ||||
| 
 | ||||
| package com.cloud.agent.api; | ||||
| 
 | ||||
| import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| public class CheckVolumeAnswer extends Answer { | ||||
| 
 | ||||
|     private long size; | ||||
|     private Map<VolumeOnStorageTO.Detail, String> volumeDetails; | ||||
| 
 | ||||
|     CheckVolumeAnswer() { | ||||
|     } | ||||
| 
 | ||||
|     public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) { | ||||
|         super(cmd, true, details); | ||||
|     public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size, | ||||
|                              Map<VolumeOnStorageTO.Detail, String> volumeDetails) { | ||||
|         super(cmd, success, details); | ||||
|         this.size = size; | ||||
|         this.volumeDetails = volumeDetails; | ||||
|     } | ||||
| 
 | ||||
|     public long getSize() { | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() { | ||||
|         return volumeDetails; | ||||
|     } | ||||
| 
 | ||||
|     public String getString() { | ||||
|         return "CheckVolumeAnswer [size=" + size + "]"; | ||||
|     } | ||||
|  | ||||
| @ -17,21 +17,28 @@ | ||||
| 
 | ||||
| package com.cloud.agent.api; | ||||
| 
 | ||||
| import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| public class CopyRemoteVolumeAnswer extends Answer { | ||||
| 
 | ||||
|     private String remoteIp; | ||||
|     private String filename; | ||||
| 
 | ||||
|     private long size; | ||||
|     private Map<VolumeOnStorageTO.Detail, String> volumeDetails; | ||||
| 
 | ||||
|     CopyRemoteVolumeAnswer() { | ||||
|     } | ||||
| 
 | ||||
|     public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) { | ||||
|         super(cmd, true, details); | ||||
|     public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size, | ||||
|                                   Map<VolumeOnStorageTO.Detail, String> volumeDetails) { | ||||
|         super(cmd, success, details); | ||||
|         this.remoteIp = cmd.getRemoteIp(); | ||||
|         this.filename = filename; | ||||
|         this.size = size; | ||||
|         this.volumeDetails = volumeDetails; | ||||
|     } | ||||
| 
 | ||||
|     public String getRemoteIp() { | ||||
| @ -54,6 +61,10 @@ public class CopyRemoteVolumeAnswer extends Answer { | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() { | ||||
|         return volumeDetails; | ||||
|     } | ||||
| 
 | ||||
|     public String getString() { | ||||
|         return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]"; | ||||
|     } | ||||
|  | ||||
| @ -31,18 +31,25 @@ import com.cloud.resource.CommandWrapper; | ||||
| import com.cloud.resource.ResourceWrapper; | ||||
| import com.cloud.storage.Storage; | ||||
| import com.cloud.utils.exception.CloudRuntimeException; | ||||
| import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgException; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgFile; | ||||
| import org.apache.commons.collections.MapUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.log4j.Logger; | ||||
| import org.libvirt.LibvirtException; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| @ResourceWrapper(handles = CheckVolumeCommand.class) | ||||
| public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> { | ||||
| 
 | ||||
|     private static final Logger s_logger = Logger.getLogger(LibvirtCheckVolumeCommandWrapper.class); | ||||
|     private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem); | ||||
| 
 | ||||
|     @Override | ||||
|     public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { | ||||
| @ -53,34 +60,76 @@ public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<Check | ||||
|         KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid()); | ||||
| 
 | ||||
|         try { | ||||
|             if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem || | ||||
|                     storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) { | ||||
|             if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) { | ||||
|                 final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile); | ||||
|                 final String path = vol.getPath(); | ||||
|                 long size = getVirtualSizeFromFile(path); | ||||
|                 return  new CheckVolumeAnswer(command, "", size); | ||||
|                 try { | ||||
|                     KVMPhysicalDisk.checkQcow2File(path); | ||||
|                 } catch (final CloudRuntimeException e) { | ||||
|                     return new CheckVolumeAnswer(command, false, "", 0, getVolumeDetails(pool, vol)); | ||||
|                 } | ||||
| 
 | ||||
|                 long size = KVMPhysicalDisk.getVirtualSizeFromFile(path); | ||||
|                 return new CheckVolumeAnswer(command, true, "", size, getVolumeDetails(pool, vol)); | ||||
|             } else { | ||||
|                 return new Answer(command, false, "Unsupported Storage Pool"); | ||||
|             } | ||||
| 
 | ||||
|         } catch (final Exception e) { | ||||
|             s_logger.error("Error while locating disk: "+ e.getMessage()); | ||||
|             s_logger.error("Error while checking the disk: " + e.getMessage()); | ||||
|             return new Answer(command, false, result); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private long getVirtualSizeFromFile(String path) { | ||||
|     private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) { | ||||
|         Map<String, String> info = getDiskFileInfo(pool, disk, true); | ||||
|         if (MapUtils.isEmpty(info)) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>(); | ||||
| 
 | ||||
|         String backingFilePath = info.get(QemuImg.BACKING_FILE); | ||||
|         if (StringUtils.isNotBlank(backingFilePath)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath); | ||||
|         } | ||||
|         String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT); | ||||
|         if (StringUtils.isNotBlank(backingFileFormat)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat); | ||||
|         } | ||||
|         String clusterSize = info.get(QemuImg.CLUSTER_SIZE); | ||||
|         if (StringUtils.isNotBlank(clusterSize)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize); | ||||
|         } | ||||
|         String fileFormat = info.get(QemuImg.FILE_FORMAT); | ||||
|         if (StringUtils.isNotBlank(fileFormat)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat); | ||||
|         } | ||||
|         String encrypted = info.get(QemuImg.ENCRYPTED); | ||||
|         if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE)); | ||||
|         } | ||||
|         Boolean isLocked = isDiskFileLocked(pool, disk); | ||||
|         volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked)); | ||||
| 
 | ||||
|         return volumeDetails; | ||||
|     } | ||||
| 
 | ||||
|     private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) { | ||||
|         if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) { | ||||
|             return new HashMap<>(); // unknown | ||||
|         } | ||||
|         try { | ||||
|             QemuImg qemu = new QemuImg(0); | ||||
|             QemuImgFile qemuFile = new QemuImgFile(path); | ||||
|             Map<String, String> info = qemu.info(qemuFile); | ||||
|             if (info.containsKey(QemuImg.VIRTUAL_SIZE)) { | ||||
|                 return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE)); | ||||
|             } else { | ||||
|                 throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path); | ||||
|             } | ||||
|             QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat()); | ||||
|             return qemu.info(qemuFile, secure); | ||||
|         } catch (QemuImgException | LibvirtException ex) { | ||||
|             throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex); | ||||
|             logger.error("Failed to get info of disk file: " + ex.getMessage()); | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) { | ||||
|         Map<String, String> info = getDiskFileInfo(pool, disk, false); | ||||
|         return info == null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -31,18 +31,25 @@ import com.cloud.resource.CommandWrapper; | ||||
| import com.cloud.resource.ResourceWrapper; | ||||
| import com.cloud.storage.Storage; | ||||
| import com.cloud.utils.exception.CloudRuntimeException; | ||||
| import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgException; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgFile; | ||||
| import org.apache.commons.collections.MapUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.log4j.Logger; | ||||
| import org.libvirt.LibvirtException; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| @ResourceWrapper(handles = CopyRemoteVolumeCommand.class) | ||||
| public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> { | ||||
| 
 | ||||
|     private static final Logger s_logger = Logger.getLogger(LibvirtCopyRemoteVolumeCommandWrapper.class); | ||||
|     private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem); | ||||
| 
 | ||||
|     @Override | ||||
|     public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { | ||||
| @ -58,14 +65,19 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper< | ||||
|         int timeoutInSecs = command.getWait(); | ||||
| 
 | ||||
|         try { | ||||
|             if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem || | ||||
|                     storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) { | ||||
|             if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) { | ||||
|                 String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs); | ||||
|                 s_logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename); | ||||
|                 final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename); | ||||
|                 final String path = vol.getPath(); | ||||
|                 long size = getVirtualSizeFromFile(path); | ||||
|                 return new CopyRemoteVolumeAnswer(command, "", filename, size); | ||||
|                 try { | ||||
|                     KVMPhysicalDisk.checkQcow2File(path); | ||||
|                 } catch (final CloudRuntimeException e) { | ||||
|                     return new CopyRemoteVolumeAnswer(command, false, "", filename, 0, getVolumeDetails(pool, vol)); | ||||
|                 } | ||||
| 
 | ||||
|                 long size = KVMPhysicalDisk.getVirtualSizeFromFile(path); | ||||
|                 return new CopyRemoteVolumeAnswer(command, true, "", filename, size, getVolumeDetails(pool, vol)); | ||||
|             } else { | ||||
|                 String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported"; | ||||
|                 return new Answer(command, false, msg); | ||||
| @ -77,18 +89,56 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper< | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private long getVirtualSizeFromFile(String path) { | ||||
|     private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) { | ||||
|         Map<String, String> info = getDiskFileInfo(pool, disk, true); | ||||
|         if (MapUtils.isEmpty(info)) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>(); | ||||
| 
 | ||||
|         String backingFilePath = info.get(QemuImg.BACKING_FILE); | ||||
|         if (StringUtils.isNotBlank(backingFilePath)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath); | ||||
|         } | ||||
|         String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT); | ||||
|         if (StringUtils.isNotBlank(backingFileFormat)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat); | ||||
|         } | ||||
|         String clusterSize = info.get(QemuImg.CLUSTER_SIZE); | ||||
|         if (StringUtils.isNotBlank(clusterSize)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize); | ||||
|         } | ||||
|         String fileFormat = info.get(QemuImg.FILE_FORMAT); | ||||
|         if (StringUtils.isNotBlank(fileFormat)) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat); | ||||
|         } | ||||
|         String encrypted = info.get(QemuImg.ENCRYPTED); | ||||
|         if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) { | ||||
|             volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE)); | ||||
|         } | ||||
|         Boolean isLocked = isDiskFileLocked(pool, disk); | ||||
|         volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked)); | ||||
| 
 | ||||
|         return volumeDetails; | ||||
|     } | ||||
| 
 | ||||
|     private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) { | ||||
|         if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) { | ||||
|             return new HashMap<>(); // unknown | ||||
|         } | ||||
|         try { | ||||
|             QemuImg qemu = new QemuImg(0); | ||||
|             QemuImgFile qemuFile = new QemuImgFile(path); | ||||
|             Map<String, String> info = qemu.info(qemuFile); | ||||
|             if (info.containsKey(QemuImg.VIRTUAL_SIZE)) { | ||||
|                 return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE)); | ||||
|             } else { | ||||
|                 throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path); | ||||
|             } | ||||
|             QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat()); | ||||
|             return qemu.info(qemuFile, secure); | ||||
|         } catch (QemuImgException | LibvirtException ex) { | ||||
|             throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex); | ||||
|             logger.error("Failed to get info of disk file: " + ex.getMessage()); | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) { | ||||
|         Map<String, String> info = getDiskFileInfo(pool, disk, false); | ||||
|         return info == null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -36,6 +36,7 @@ import org.apache.cloudstack.utils.qemu.QemuImg; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgException; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgFile; | ||||
| import org.apache.commons.collections.MapUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.libvirt.LibvirtException; | ||||
| 
 | ||||
| @ -91,37 +92,46 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp | ||||
|             if (disk.getQemuEncryptFormat() != null) { | ||||
|                 volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString()); | ||||
|             } | ||||
|             String backingFilePath = info.get(QemuImg.BACKING_FILE); | ||||
|             if (StringUtils.isNotBlank(backingFilePath)) { | ||||
|                 volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath); | ||||
|             } | ||||
|             String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT); | ||||
|             if (StringUtils.isNotBlank(backingFileFormat)) { | ||||
|                 volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat); | ||||
|             } | ||||
|             String clusterSize = info.get(QemuImg.CLUSTER_SIZE); | ||||
|             if (StringUtils.isNotBlank(clusterSize)) { | ||||
|                 volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize); | ||||
|             } | ||||
|             String fileFormat = info.get(QemuImg.FILE_FORMAT); | ||||
|             if (StringUtils.isNotBlank(fileFormat)) { | ||||
|                 if (!fileFormat.equalsIgnoreCase(disk.getFormat().toString())) { | ||||
|                     return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat())); | ||||
|                 } | ||||
|                 volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat); | ||||
|             if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) { | ||||
|                 return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat())); | ||||
|             } | ||||
|             String encrypted = info.get(QemuImg.ENCRYPTED); | ||||
|             if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) { | ||||
|                 volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE)); | ||||
|             } | ||||
|             Boolean isLocked = isDiskFileLocked(storagePool, disk); | ||||
|             volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked)); | ||||
|             addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk); | ||||
| 
 | ||||
|             volumes.add(volumeOnStorageTO); | ||||
|         } | ||||
|         return new GetVolumesOnStorageAnswer(command, volumes); | ||||
|     } | ||||
| 
 | ||||
|     private void addDetailsToVolumeOnStorageTO(VolumeOnStorageTO volumeOnStorageTO, final Map<String, String> info, final KVMStoragePool storagePool, final KVMPhysicalDisk disk) { | ||||
|         if (MapUtils.isEmpty(info)) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         String backingFilePath = info.get(QemuImg.BACKING_FILE); | ||||
|         if (StringUtils.isNotBlank(backingFilePath)) { | ||||
|             volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath); | ||||
|         } | ||||
|         String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT); | ||||
|         if (StringUtils.isNotBlank(backingFileFormat)) { | ||||
|             volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat); | ||||
|         } | ||||
|         String clusterSize = info.get(QemuImg.CLUSTER_SIZE); | ||||
|         if (StringUtils.isNotBlank(clusterSize)) { | ||||
|             volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize); | ||||
|         } | ||||
|         String fileFormat = info.get(QemuImg.FILE_FORMAT); | ||||
|         if (StringUtils.isNotBlank(fileFormat)) { | ||||
|             volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat); | ||||
|         } | ||||
|         String encrypted = info.get(QemuImg.ENCRYPTED); | ||||
|         if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) { | ||||
|             volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE)); | ||||
|         } | ||||
|         Boolean isLocked = isDiskFileLocked(storagePool, disk); | ||||
|         volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked)); | ||||
|     } | ||||
| 
 | ||||
|     private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand command, final KVMStoragePool storagePool, String keyword) { | ||||
|         List<VolumeOnStorageTO> volumes = new ArrayList<>(); | ||||
| 
 | ||||
| @ -134,11 +144,21 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp | ||||
|             if (!isDiskFormatSupported(disk)) { | ||||
|                 continue; | ||||
|             } | ||||
|             Map<String, String> info = getDiskFileInfo(storagePool, disk, true); | ||||
|             if (info == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, disk.getName(), disk.getName(), disk.getPath(), | ||||
|                     disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize()); | ||||
|             if (disk.getQemuEncryptFormat() != null) { | ||||
|                 volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString()); | ||||
|             } | ||||
|             String fileFormat = info.get(QemuImg.FILE_FORMAT); | ||||
|             if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) { | ||||
|                 continue; | ||||
|             } | ||||
|             addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk); | ||||
| 
 | ||||
|             volumes.add(volumeOnStorageTO); | ||||
|         } | ||||
|         return new GetVolumesOnStorageAnswer(command, volumes); | ||||
|  | ||||
| @ -25,7 +25,6 @@ import java.io.IOException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor; | ||||
| import org.apache.cloudstack.utils.cryptsetup.KeyFile; | ||||
| @ -33,7 +32,6 @@ import org.apache.cloudstack.utils.qemu.QemuImageOptions; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgException; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgFile; | ||||
| import org.apache.cloudstack.utils.qemu.QemuObject; | ||||
| import org.apache.log4j.Logger; | ||||
| import org.libvirt.Connect; | ||||
| @ -102,7 +100,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi | ||||
|                 newSize = ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(newSize); | ||||
|             } else if (spool.getType().equals(StoragePoolType.PowerFlex)) { | ||||
|                 // PowerFlex RAW/LUKS is already resized, we just notify the domain based on new size (considering LUKS overhead) | ||||
|                 newSize = getVirtualSizeFromFile(path); | ||||
|                 newSize = KVMPhysicalDisk.getVirtualSizeFromFile(path); | ||||
|             } | ||||
| 
 | ||||
|             if (pool.getType() != StoragePoolType.RBD && pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex) { | ||||
| @ -216,21 +214,6 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private long getVirtualSizeFromFile(String path) { | ||||
|         try { | ||||
|             QemuImg qemu = new QemuImg(0); | ||||
|             QemuImgFile qemuFile = new QemuImgFile(path); | ||||
|             Map<String, String> info = qemu.info(qemuFile); | ||||
|             if (info.containsKey(QemuImg.VIRTUAL_SIZE)) { | ||||
|                 return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE)); | ||||
|             } else { | ||||
|                 throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path); | ||||
|             } | ||||
|         } catch (QemuImgException | LibvirtException ex) { | ||||
|             throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private Answer handleMultipathSCSIResize(ResizeVolumeCommand command, KVMStoragePool pool) { | ||||
|         ((MultipathSCSIPool)pool).resize(command.getPath(), command.getInstanceName(), command.getNewSize()); | ||||
|         return new ResizeVolumeAnswer(command, true, ""); | ||||
|  | ||||
| @ -16,13 +16,21 @@ | ||||
| // under the License. | ||||
| package com.cloud.hypervisor.kvm.storage; | ||||
| 
 | ||||
| import com.cloud.utils.exception.CloudRuntimeException; | ||||
| import org.apache.cloudstack.storage.formatinspector.Qcow2Inspector; | ||||
| import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgException; | ||||
| import org.apache.cloudstack.utils.qemu.QemuImgFile; | ||||
| import org.apache.cloudstack.utils.qemu.QemuObject; | ||||
| import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.libvirt.LibvirtException; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| public class KVMPhysicalDisk { | ||||
|     private String path; | ||||
| @ -71,6 +79,31 @@ public class KVMPhysicalDisk { | ||||
|         return hostIp; | ||||
|     } | ||||
| 
 | ||||
|     public static long getVirtualSizeFromFile(String path) { | ||||
|         try { | ||||
|             QemuImg qemu = new QemuImg(0); | ||||
|             QemuImgFile qemuFile = new QemuImgFile(path); | ||||
|             Map<String, String> info = qemu.info(qemuFile); | ||||
|             if (info.containsKey(QemuImg.VIRTUAL_SIZE)) { | ||||
|                 return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE)); | ||||
|             } else { | ||||
|                 throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path); | ||||
|             } | ||||
|         } catch (QemuImgException | LibvirtException ex) { | ||||
|             throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void checkQcow2File(String path) { | ||||
|         if (ImageStoreUtil.isCorrectExtension(path, "qcow2")) { | ||||
|             try { | ||||
|                 Qcow2Inspector.validateQcow2File(path); | ||||
|             } catch (RuntimeException e) { | ||||
|                 throw new CloudRuntimeException("The volume file at path " + path + " is not a valid QCOW2. Error: " + e.getMessage()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private PhysicalDiskFormat format; | ||||
|     private long size; | ||||
|     private long virtualSize; | ||||
|  | ||||
| @ -170,6 +170,7 @@ import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; | ||||
| import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; | ||||
| import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; | ||||
| import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; | ||||
| import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; | ||||
| import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; | ||||
| import org.apache.commons.collections.CollectionUtils; | ||||
| import org.apache.commons.collections.MapUtils; | ||||
| @ -812,7 +813,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { | ||||
|             throw new CloudRuntimeException("Error while copying volume of remote instance: " + answer.getDetails()); | ||||
|         } | ||||
|         CopyRemoteVolumeAnswer copyRemoteVolumeAnswer = (CopyRemoteVolumeAnswer) answer; | ||||
|         if(!copyRemoteVolumeAnswer.getResult()) { | ||||
|         checkVolume(copyRemoteVolumeAnswer.getVolumeDetails()); | ||||
|         if (!copyRemoteVolumeAnswer.getResult()) { | ||||
|             throw new CloudRuntimeException("Unable to copy volume of remote instance"); | ||||
|         } | ||||
|         diskProfile.setSize(copyRemoteVolumeAnswer.getSize()); | ||||
| @ -2653,7 +2655,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { | ||||
|             throw new CloudRuntimeException("Disk not found or is invalid"); | ||||
|         } | ||||
|         CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer; | ||||
|         if(!checkVolumeAnswer.getResult()) { | ||||
|         try { | ||||
|             checkVolume(checkVolumeAnswer.getVolumeDetails()); | ||||
|         } catch (CloudRuntimeException e) { | ||||
|             cleanupFailedImportVM(userVm); | ||||
|             throw e; | ||||
|         } | ||||
|         if (!checkVolumeAnswer.getResult()) { | ||||
|             cleanupFailedImportVM(userVm); | ||||
|             throw new CloudRuntimeException("Disk not found or is invalid"); | ||||
|         } | ||||
| @ -2679,6 +2687,31 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { | ||||
|         return userVm; | ||||
|     } | ||||
| 
 | ||||
|     private void checkVolume(Map<VolumeOnStorageTO.Detail, String> volumeDetails) { | ||||
|         if (MapUtils.isEmpty(volumeDetails)) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.IS_LOCKED)) { | ||||
|             String isLocked = volumeDetails.get(VolumeOnStorageTO.Detail.IS_LOCKED); | ||||
|             if (Boolean.parseBoolean(isLocked)) { | ||||
|                 logFailureAndThrowException("Locked volume cannot be imported or unmanaged."); | ||||
|             } | ||||
|         } | ||||
|         if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.IS_ENCRYPTED)) { | ||||
|             String isEncrypted = volumeDetails.get(VolumeOnStorageTO.Detail.IS_ENCRYPTED); | ||||
|             if (Boolean.parseBoolean(isEncrypted)) { | ||||
|                 logFailureAndThrowException("Encrypted volume cannot be imported or unmanaged."); | ||||
|             } | ||||
|         } | ||||
|         if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.BACKING_FILE)) { | ||||
|             String backingFile = volumeDetails.get(VolumeOnStorageTO.Detail.BACKING_FILE); | ||||
|             if (StringUtils.isNotBlank(backingFile)) { | ||||
|                 logFailureAndThrowException("Volume with backing file cannot be imported or unmanaged."); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean selectAny) throws InsufficientCapacityException, ResourceAllocationException { | ||||
|         NetworkVO defaultNetwork = null; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user