diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java index 0cecbb37020..bd3f39a09aa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java @@ -72,6 +72,7 @@ public class ListCapabilitiesCmd extends BaseCmd { response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME)); response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT)); response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE)); + response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED)); response.setObjectName("capability"); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index 3861ac455ed..ff2e33b1389 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -136,6 +136,10 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "the min Ram size for the service offering used by the shared filesystem instance", since = "4.20.0") private Integer sharedFsVmMinRamSize; + @SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED) + @Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0") + private Boolean dynamicScalingEnabled; + public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) { this.securityGroupsEnabled = securityGroupsEnabled; } @@ -247,4 +251,8 @@ public class CapabilitiesResponse extends BaseResponse { public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) { this.sharedFsVmMinRamSize = sharedFsVmMinRamSize; } + + public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } } diff --git a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java index 5a32ab59a7a..07b7e102df9 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java @@ -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 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 volumeDetails) { + super(cmd, success, details); this.size = size; + this.volumeDetails = volumeDetails; } public long getSize() { return size; } + public Map getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CheckVolumeAnswer [size=" + size + "]"; } diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java index e79005be71b..4aec0b26581 100644 --- a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java @@ -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 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 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 getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]"; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java index c8b0aafd0d6..55225ba086c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java @@ -31,16 +31,24 @@ 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.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 { + private static final List STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem); + @Override public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { String result = null; @@ -50,34 +58,76 @@ public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) { + Map info = getDiskFileInfo(pool, disk, true); + if (MapUtils.isEmpty(info)) { + return null; + } + + Map 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 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 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 info = getDiskFileInfo(pool, disk, false); + return info == null; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java index e6ec05fec23..04a94dae830 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java @@ -31,15 +31,22 @@ 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.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 { + private static final List STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem); @Override public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { @@ -55,14 +62,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); 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); @@ -74,18 +86,56 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper< } } - private long getVirtualSizeFromFile(String path) { + private Map getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) { + Map info = getDiskFileInfo(pool, disk, true); + if (MapUtils.isEmpty(info)) { + return null; + } + + Map 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 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 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 info = getDiskFileInfo(pool, disk, false); + return info == null; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java index 821a80f5cca..6facf169602 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java @@ -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 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 volumes = new ArrayList<>(); @@ -134,11 +144,21 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp if (!isDiskFormatSupported(disk)) { continue; } + Map 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); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java index 6a3901e345c..f2af46d4cc8 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java @@ -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.libvirt.Connect; import org.libvirt.Domain; @@ -100,7 +98,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper 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, ""); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java index 9d9a6415e27..c43f5101fbe 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java @@ -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 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; diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index 2abda3ebc50..a96b7c75b2b 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -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). +## [2025-07-01] + +### Fixed + +- Regression in 4.19.3 and 4.21.0 with templates from snapshots + ## [2025-05-07] ### Added diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index ba4a7b14787..28928570732 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -622,7 +622,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { try { templateProps.load(new FileInputStream(propFile.toFile())); String desc = templateProps.getProperty("description"); - if (desc.startsWith("SystemVM Template")) { + if (desc != null && desc.startsWith("SystemVM Template")) { return true; } } catch (IOException e) { diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index 574e5ddcfea..a0999090355 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -74,12 +74,14 @@ import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStorageResourceAssoc; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotDetailsDao; import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; @@ -133,6 +135,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver ConfigurationDao _configDao; @Inject private HostDao _hostDao; + @Inject private VMTemplateDao _vmTemplateDao; private long volumeStatsLastUpdate = 0L; private final Map> volumeStats = new HashMap<>(); @@ -670,8 +673,15 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver storagePoolVO.getId(), csCloneId, null); if (tmplPoolRef != null) { - final String templateRscName = LinstorUtil.RSC_PREFIX + tmplPoolRef.getLocalDownloadPath(); + final String templateRscName; + if (tmplPoolRef.getLocalDownloadPath() == null) { + VMTemplateVO vmTemplateVO = _vmTemplateDao.findById(tmplPoolRef.getTemplateId()); + templateRscName = LinstorUtil.RSC_PREFIX + vmTemplateVO.getUuid(); + } else { + templateRscName = LinstorUtil.RSC_PREFIX + tmplPoolRef.getLocalDownloadPath(); + } final String rscName = LinstorUtil.RSC_PREFIX + volumeInfo.getUuid(); + final DevelopersApi linstorApi = LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress()); try { diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 7c8c9ae28c8..6fb9ab515cb 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -164,6 +165,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; import com.cloud.api.query.dao.AccountJoinDao; @@ -4334,11 +4336,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q */ private void buildSearchCriteriaForDomainAdmins(SearchCriteria sc, Account account) { List domainIds = new ArrayList<>(); - DomainVO domainRecord = _domainDao.findById(account.getDomainId()); - if (domainRecord == null) { - logger.error("Could not find the domainId for account: {}", account); - throw new CloudAuthenticationException("Could not find the domainId for account:" + account.getAccountName()); - } + DomainVO domainRecord = getDomainForAccount(account); logger.trace("adding caller's domain {} to the list of domains to search for zones", account.getDomainId()); domainIds.add(domainRecord.getId()); // find all domain Ids till leaf @@ -4371,6 +4369,16 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } } + @NotNull + private DomainVO getDomainForAccount(Account account) { + DomainVO domainRecord = _domainDao.findById(account.getDomainId()); + if (domainRecord == null) { + logger.error("Could not find the domainId for account: {}", account); + throw new CloudAuthenticationException("Could not find the domainId for account:" + account.getAccountName()); + } + return domainRecord; + } + /** * Return all zones for the user's domain, and everything above till root * list all zones belonging to this domain, and all of its parents @@ -4380,11 +4388,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q // find all domain Id up to root domain for this account List domainIds = new ArrayList<>(); - DomainVO domainRecord = _domainDao.findById(account.getDomainId()); - if (domainRecord == null) { - logger.error("Could not find the domainId for account: {}", account); - throw new CloudAuthenticationException("Could not find the domainId for account:" + account.getAccountName()); - } + DomainVO domainRecord = getDomainForAccount(account); domainIds.add(domainRecord.getId()); while (domainRecord.getParent() != null) { domainRecord = _domainDao.findById(domainRecord.getParent()); @@ -4495,6 +4499,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q boolean showRemovedTmpl = cmd.getShowRemoved(); Account caller = CallContext.current().getCallingAccount(); Long parentTemplateId = cmd.getParentTemplateId(); + Long domainId = cmd.getDomainId(); + boolean isRecursive = cmd.isRecursive(); boolean listAll = false; if (templateFilter != null && templateFilter == TemplateFilter.all) { @@ -4505,7 +4511,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } List permittedAccountIds = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(domainId, isRecursive, null); accountMgr.buildACLSearchParameters( caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false @@ -4533,7 +4539,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique(), - templateType, isVnf, cmd.getArch()); + templateType, isVnf, domainId, isRecursive, cmd.getArch()); } private Pair, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, @@ -4542,7 +4548,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q boolean showDomr, boolean onlyReady, List permittedAccounts, Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags, boolean showRemovedTmpl, List ids, Long parentTemplateId, Boolean showUnique, String templateType, - Boolean isVnf, CPU.CPUArch arch) { + Boolean isVnf, Long domainId, boolean isRecursive, CPU.CPUArch arch) { // check if zone is configured, if not, just return empty list List hypers = null; @@ -4628,7 +4634,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q if (!permittedAccounts.isEmpty()) { domain = _domainDao.findById(permittedAccounts.get(0).getDomainId()); } else { - domain = _domainDao.findById(caller.getDomainId()); + domain = _domainDao.findById(Objects.requireNonNullElse(domainId, caller.getDomainId())); } setIdsListToSearchCriteria(sc, ids); @@ -4640,10 +4646,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("accountType", SearchCriteria.Op.EQ, Account.Type.PROJECT); } - // add criteria for domain path in case of domain admin + // add criteria for domain path in case of admins if ((templateFilter == TemplateFilter.self || templateFilter == TemplateFilter.selfexecutable) - && (caller.getType() == Account.Type.DOMAIN_ADMIN || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN)) { - sc.addAnd("domainPath", SearchCriteria.Op.LIKE, domain.getPath() + "%"); + && (accountMgr.isAdmin(caller.getAccountId()))) { + if (isRecursive) { + sc.addAnd("domainPath", SearchCriteria.Op.LIKE, domain.getPath() + "%"); + } else { + sc.addAnd("domainPath", SearchCriteria.Op.EQ, domain.getPath()); + } } List relatedDomainIds = new ArrayList<>(); @@ -4949,6 +4959,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Map tags = cmd.getTags(); boolean showRemovedISO = cmd.getShowRemoved(); Account caller = CallContext.current().getCallingAccount(); + Long domainId = cmd.getDomainId(); + boolean isRecursive = cmd.isRecursive(); boolean listAll = false; if (isoFilter != null && isoFilter == TemplateFilter.all) { @@ -4960,7 +4972,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q List permittedAccountIds = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(domainId, isRecursive, null); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); List permittedAccounts = new ArrayList<>(); @@ -4973,7 +4985,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(), hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, - tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null, cmd.getArch()); + tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null, + domainId, isRecursive, cmd.getArch()); } @Override @@ -5051,7 +5064,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q options.put(VmDetailConstants.VIDEO_HARDWARE, Arrays.asList("cirrus", "vga", "qxl", "virtio")); options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList()); options.put(VmDetailConstants.IO_POLICY, Arrays.asList("threads", "native", "io_uring", "storage_specific")); - options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled")); + options.put(VmDetailConstants.IOTHREADS, List.of("enabled")); options.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, Collections.emptyList()); options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false")); options.put(VmDetailConstants.VIRTUAL_TPM_MODEL, Arrays.asList("tpm-tis", "tpm-crb")); @@ -5090,7 +5103,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Long domainId = cmd.getDomainId(); final Long projectId = cmd.getProjectId(); Boolean isRecursive = cmd.isRecursive(); - final Boolean listAll = cmd.listAll(); + final boolean listAll = cmd.listAll(); final Long startIndex = cmd.getStartIndex(); final Long pageSize = cmd.getPageSizeVal(); final String keyword = cmd.getKeyword(); diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index ac24f14b781..dc946a2a207 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -476,6 +476,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation 0) { isoResponse.setSize(isoSize); } + long isoPhysicalSize = iso.getPhysicalSize(); + if (isoPhysicalSize > 0) { + isoResponse.setPhysicalSize(isoPhysicalSize); + } if (iso.getUserDataId() != null) { isoResponse.setUserDataId(iso.getUserDataUUid()); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 9e1f2332cac..271372bf656 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -4528,6 +4528,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe capabilities.put(ApiConstants.INSTANCES_STATS_USER_ONLY, StatsCollector.vmStatsCollectUserVMOnly.value()); capabilities.put(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED, StatsCollector.vmDiskStatsRetentionEnabled.value()); capabilities.put(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME, StatsCollector.vmDiskStatsMaxRetentionTime.value()); + capabilities.put(ApiConstants.DYNAMIC_SCALING_ENABLED, UserVmManager.EnableDynamicallyScaleVm.value()); if (apiLimitEnabled) { capabilities.put("apiLimitInterval", apiLimitInterval); capabilities.put("apiLimitMax", apiLimitMax); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 4de494bb554..865884c2538 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -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; @@ -814,7 +815,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()); @@ -2700,7 +2702,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"); } @@ -2726,6 +2734,31 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return userVm; } + private void checkVolume(Map 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; diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java index 2cc61dae133..a580105d52a 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import io.netty.util.IllegalReferenceCountException; import org.apache.cloudstack.storage.template.UploadEntity; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.commons.lang3.StringUtils; @@ -230,8 +231,15 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler { diff --git a/ui/src/views/compute/DeployVnfAppliance.vue b/ui/src/views/compute/DeployVnfAppliance.vue index 5e447131bc9..1117413d710 100644 --- a/ui/src/views/compute/DeployVnfAppliance.vue +++ b/ui/src/views/compute/DeployVnfAppliance.vue @@ -957,8 +957,7 @@ export default { keyboards: [], bootTypes: [], bootModes: [], - ioPolicyTypes: [], - dynamicScalingVmConfig: false + ioPolicyTypes: [] }, rowCount: {}, loading: { @@ -1191,13 +1190,6 @@ export default { type: 'Routing' }, field: 'hostid' - }, - dynamicScalingVmConfig: { - list: 'listConfigurations', - options: { - zoneid: _.get(this.zone, 'id'), - name: 'enable.dynamic.scale.vm' - } } } }, @@ -1329,7 +1321,7 @@ export default { return Boolean('listUserData' in this.$store.getters.apis) }, dynamicScalingVmConfigValue () { - return this.options.dynamicScalingVmConfig?.[0]?.value === 'true' + return this.$store.getters.features.dynamicscalingenabled }, isCustomizedDiskIOPS () { return this.diskSelected?.iscustomizediops || false @@ -2403,7 +2395,7 @@ export default { param.loading = true param.opts = [] const options = param.options || {} - if (!('listall' in options) && !['zones', 'pods', 'clusters', 'hosts', 'dynamicScalingVmConfig', 'hypervisors'].includes(name)) { + if (!('listall' in options) && !['zones', 'pods', 'clusters', 'hosts', 'hypervisors'].includes(name)) { options.listall = true } api(param.list, options).then((response) => { diff --git a/ui/src/views/compute/EditVM.vue b/ui/src/views/compute/EditVM.vue index b534045f4bf..d5e75fcc658 100644 --- a/ui/src/views/compute/EditVM.vue +++ b/ui/src/views/compute/EditVM.vue @@ -152,7 +152,6 @@ export default { template: {}, userDataEnabled: false, securityGroupsEnabled: false, - dynamicScalingVmConfig: false, loading: false, securitygroups: { loading: false, @@ -197,7 +196,6 @@ export default { this.fetchInstaceGroups() this.fetchServiceOfferingData() this.fetchTemplateData() - this.fetchDynamicScalingVmConfig() this.fetchUserData() }, fetchZoneDetails () { @@ -249,18 +247,8 @@ export default { this.template = templateResponses[0] }) }, - fetchDynamicScalingVmConfig () { - const params = {} - params.name = 'enable.dynamic.scale.vm' - params.zoneid = this.resource.zoneid - var apiName = 'listConfigurations' - api(apiName, params).then(json => { - const configResponse = json.listconfigurationsresponse.configuration - this.dynamicScalingVmConfig = configResponse[0]?.value === 'true' - }) - }, - canDynamicScalingEnabled () { - return this.template.isdynamicallyscalable && this.serviceOffering.dynamicscalingenabled && this.dynamicScalingVmConfig + isDynamicScalingEnabled () { + return this.template.isdynamicallyscalable && this.serviceOffering.dynamicscalingenabled && this.$store.getters.features.dynamicscalingenabled }, fetchOsTypes () { this.osTypes.loading = true diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java index 189797a3cb2..d3c3a9d465e 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -41,7 +41,6 @@ import com.vmware.vim25.HostConnectInfo; import com.vmware.vim25.HostFirewallInfo; import com.vmware.vim25.HostFirewallRuleset; import com.vmware.vim25.HostHardwareSummary; -import com.vmware.vim25.HostHyperThreadScheduleInfo; import com.vmware.vim25.HostIpConfig; import com.vmware.vim25.HostIpRouteEntry; import com.vmware.vim25.HostListSummaryQuickStats; @@ -94,10 +93,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { return _context.getVimClient().getDynamicProperty(_mor, "configManager"); } - public List getHostVirtualNicManagerNetConfig() throws Exception { - return _context.getVimClient().getDynamicProperty(_mor, "config.virtualNicManagerInfo.netConfig"); - } - public List getHostIpRouteEntries() throws Exception { return _context.getVimClient().getDynamicProperty(_mor, "config.network.routeTableInfo.ipRoute"); } @@ -106,10 +101,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { return _context.getVimClient().getDynamicProperty(_mor, "summary.quickStats"); } - public HostHyperThreadScheduleInfo getHostHyperThreadInfo() throws Exception { - return _context.getVimClient().getDynamicProperty(_mor, "config.hyperThread"); - } - public HostNetworkInfo getHostNetworkInfo() throws Exception { return _context.getVimClient().getDynamicProperty(_mor, "config.network"); } @@ -260,7 +251,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { @Override public ManagedObjectReference getHyperHostDatacenter() throws Exception { Pair dcPair = DatacenterMO.getOwnerDatacenter(getContext(), getMor()); - assert (dcPair != null); return dcPair.first().getMor(); } @@ -282,19 +272,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { throw new Exception("Standalone host is not supported"); } - public ManagedObjectReference[] getHostLocalDatastore() throws Exception { - List datastores = _context.getVimClient().getDynamicProperty(_mor, "datastore"); - List l = new ArrayList<>(); - if (datastores != null) { - for (ManagedObjectReference mor : datastores) { - DatastoreSummary summary = _context.getVimClient().getDynamicProperty(mor, "summary"); - if (summary.getType().equalsIgnoreCase("VMFS") && !summary.isMultipleHostAccess()) - l.add(mor); - } - } - return l.toArray(new ManagedObjectReference[1]); - } - public HostVirtualSwitch getHostVirtualSwitchByName(String name) throws Exception { List switches = _context.getVimClient().getDynamicProperty(_mor, "config.network.vswitch"); @@ -307,16 +284,17 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { return null; } - public List getHostVirtualSwitch() throws Exception { - return _context.getVimClient().getDynamicProperty(_mor, "config.network.vswitch"); - } - public AboutInfo getHostAboutInfo() throws Exception { return _context.getVimClient().getDynamicProperty(_mor, "config.product"); } public VmwareHostType getHostType() throws Exception { AboutInfo aboutInfo = getHostAboutInfo(); + if (aboutInfo == null) { + String msg = "no type info about host known, assuming ESXi"; + logger.warn(msg); + return VmwareHostType.ESXi; + } if ("VMware ESXi".equals(aboutInfo.getName())) return VmwareHostType.ESXi; else if ("VMware ESX".equals(aboutInfo.getName())) @@ -325,50 +303,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { throw new Exception("Unrecognized VMware host type " + aboutInfo.getName()); } - // default virtual switch is which management network residents on - public HostVirtualSwitch getHostDefaultVirtualSwitch() throws Exception { - String managementPortGroup = getPortGroupNameByNicType(HostVirtualNicType.management); - if (managementPortGroup != null) - return getPortGroupVirtualSwitch(managementPortGroup); - - return null; - } - - public HostVirtualSwitch getPortGroupVirtualSwitch(String portGroupName) throws Exception { - String vSwitchName = getPortGroupVirtualSwitchName(portGroupName); - if (vSwitchName != null) - return getVirtualSwitchByName(vSwitchName); - - return null; - } - - public HostVirtualSwitch getVirtualSwitchByName(String vSwitchName) throws Exception { - - List vSwitchs = getHostVirtualSwitch(); - if (vSwitchs != null) { - for (HostVirtualSwitch vSwitch : vSwitchs) { - if (vSwitch.getName().equals(vSwitchName)) - return vSwitch; - } - } - - return null; - } - - public String getPortGroupVirtualSwitchName(String portGroupName) throws Exception { - HostNetworkInfo hostNetInfo = getHostNetworkInfo(); - List portGroups = hostNetInfo.getPortgroup(); - if (portGroups != null) { - for (HostPortGroup portGroup : portGroups) { - HostPortGroupSpec spec = portGroup.getSpec(); - if (spec.getName().equals(portGroupName)) - return spec.getVswitchName(); - } - } - - return null; - } - public HostPortGroupSpec getPortGroupSpec(String portGroupName) throws Exception { HostNetworkInfo hostNetInfo = getHostNetworkInfo(); List portGroups = hostNetInfo.getPortgroup(); @@ -383,40 +317,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { return null; } - public String getPortGroupNameByNicType(HostVirtualNicType nicType) throws Exception { - assert (nicType != null); - - List netConfigs = - _context.getVimClient().getDynamicProperty(_mor, "config.virtualNicManagerInfo.netConfig"); - - if (netConfigs != null) { - for (VirtualNicManagerNetConfig netConfig : netConfigs) { - if (netConfig.getNicType().equals(nicType.toString())) { - List nics = netConfig.getCandidateVnic(); - if (nics != null) { - for (HostVirtualNic nic : nics) { - return nic.getPortgroup(); - } - } - } - } - } - - if (nicType == HostVirtualNicType.management) { - // ESX management network is configured in service console - HostNetworkInfo netInfo = getHostNetworkInfo(); - assert (netInfo != null); - List nics = netInfo.getConsoleVnic(); - if (nics != null) { - for (HostVirtualNic nic : nics) { - return nic.getPortgroup(); - } - } - } - - return null; - } - public boolean hasOpaqueNSXNetwork() throws Exception{ HostNetworkInfo netInfo = getHostNetworkInfo(); List opaqueNetworks = netInfo.getOpaqueNetwork(); @@ -436,9 +336,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { public boolean hasPortGroup(HostVirtualSwitch vSwitch, String portGroupName) throws Exception { ManagedObjectReference morNetwork = getNetworkMor(portGroupName); - if (morNetwork != null) - return true; - return false; + return morNetwork != null; } public void createPortGroup(HostVirtualSwitch vSwitch, String portGroupName, Integer vlanId, HostNetworkSecurityPolicy secPolicy, @@ -451,7 +349,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { spec.setName(portGroupName); if (vlanId != null) - spec.setVlanId(vlanId.intValue()); + spec.setVlanId(vlanId); HostNetworkPolicy policy = new HostNetworkPolicy(); if (secPolicy != null) policy.setSecurity(secPolicy); @@ -471,7 +369,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { spec.setName(portGroupName); if (vlanId != null) - spec.setVlanId(vlanId.intValue()); + spec.setVlanId(vlanId); HostNetworkPolicy policy = new HostNetworkPolicy(); if (secPolicy != null) policy.setSecurity(secPolicy); @@ -525,15 +423,8 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { return null; } - public List getVmMorsOnNetwork(String portGroupName) throws Exception { - ManagedObjectReference morNetwork = getNetworkMor(portGroupName); - if (morNetwork != null) - return _context.getVimClient().getDynamicProperty(morNetwork, "vm"); - return null; - } - public String getHostName() throws Exception { - return (String)_context.getVimClient().getDynamicProperty(_mor, "name"); + return _context.getVimClient().getDynamicProperty(_mor, "name"); } @Override @@ -572,11 +463,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { String internalCSUserVMNamingPattern = "^[i][-][0-9]+[-][0-9]+[-]"; Pattern p = Pattern.compile(internalCSUserVMNamingPattern); java.util.regex.Matcher m = p.matcher(vmInternalCSName); - if (m.find()) { - return true; - } else { - return false; - } + return m.find(); } private void loadVmCache() throws Exception { @@ -723,7 +610,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { PropertyFilterSpec pfSpec = new PropertyFilterSpec(); pfSpec.getPropSet().add(pSpec); pfSpec.getObjectSet().add(oSpec); - List pfSpecArr = new ArrayList(); + List pfSpecArr = new ArrayList<>(); pfSpecArr.add(pfSpec); List properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr); @@ -756,7 +643,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { PropertyFilterSpec pfSpec = new PropertyFilterSpec(); pfSpec.getPropSet().add(pSpec); pfSpec.getObjectSet().add(oSpec); - List pfSpecArr = new ArrayList(); + List pfSpecArr = new ArrayList<>(); pfSpecArr.add(pfSpec); List properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr); @@ -799,21 +686,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { return dsList; } - public void importVmFromOVF(String ovfFilePath, String vmName, String datastoreName, String diskOption, String configurationId) throws Exception { - if (logger.isTraceEnabled()) - logger.trace("vCenter API trace - importVmFromOVF(). target MOR: " + _mor.getValue() + ", ovfFilePath: " + ovfFilePath + ", vmName: " + vmName + - ",datastoreName: " + datastoreName + ", diskOption: " + diskOption); - - DatastoreMO dsMo = getHostDatastoreMO(datastoreName); - if (dsMo == null) - throw new Exception("Invalid datastore name: " + datastoreName); - - importVmFromOVF(ovfFilePath, vmName, dsMo, diskOption, configurationId); - - if (logger.isTraceEnabled()) - logger.trace("vCenter API trace - importVmFromOVF() done"); - } - @Override public void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, String configurationId) throws Exception {