Merge branch '4.19' into 4.20

This commit is contained in:
Daan Hoogland 2025-07-25 22:01:17 +02:00
commit 609efcc231
24 changed files with 440 additions and 282 deletions

View File

@ -72,6 +72,7 @@ public class ListCapabilitiesCmd extends BaseCmd {
response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME)); response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT)); response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT));
response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE)); response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE));
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
response.setObjectName("capability"); response.setObjectName("capability");
response.setResponseName(getCommandName()); response.setResponseName(getCommandName());
this.setResponseObject(response); this.setResponseObject(response);

View File

@ -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") @Param(description = "the min Ram size for the service offering used by the shared filesystem instance", since = "4.20.0")
private Integer sharedFsVmMinRamSize; 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) { public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
this.securityGroupsEnabled = securityGroupsEnabled; this.securityGroupsEnabled = securityGroupsEnabled;
} }
@ -247,4 +251,8 @@ public class CapabilitiesResponse extends BaseResponse {
public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) { public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) {
this.sharedFsVmMinRamSize = sharedFsVmMinRamSize; this.sharedFsVmMinRamSize = sharedFsVmMinRamSize;
} }
public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) {
this.dynamicScalingEnabled = dynamicScalingEnabled;
}
} }

View File

@ -17,22 +17,33 @@
package com.cloud.agent.api; package com.cloud.agent.api;
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
import java.util.Map;
public class CheckVolumeAnswer extends Answer { public class CheckVolumeAnswer extends Answer {
private long size; private long size;
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
CheckVolumeAnswer() { CheckVolumeAnswer() {
} }
public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) { public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size,
super(cmd, true, details); Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
super(cmd, success, details);
this.size = size; this.size = size;
this.volumeDetails = volumeDetails;
} }
public long getSize() { public long getSize() {
return size; return size;
} }
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
return volumeDetails;
}
public String getString() { public String getString() {
return "CheckVolumeAnswer [size=" + size + "]"; return "CheckVolumeAnswer [size=" + size + "]";
} }

View File

@ -17,21 +17,28 @@
package com.cloud.agent.api; package com.cloud.agent.api;
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
import java.util.Map;
public class CopyRemoteVolumeAnswer extends Answer { public class CopyRemoteVolumeAnswer extends Answer {
private String remoteIp; private String remoteIp;
private String filename; private String filename;
private long size; private long size;
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
CopyRemoteVolumeAnswer() { CopyRemoteVolumeAnswer() {
} }
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) { public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size,
super(cmd, true, details); Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
super(cmd, success, details);
this.remoteIp = cmd.getRemoteIp(); this.remoteIp = cmd.getRemoteIp();
this.filename = filename; this.filename = filename;
this.size = size; this.size = size;
this.volumeDetails = volumeDetails;
} }
public String getRemoteIp() { public String getRemoteIp() {
@ -54,6 +61,10 @@ public class CopyRemoteVolumeAnswer extends Answer {
return size; return size;
} }
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
return volumeDetails;
}
public String getString() { public String getString() {
return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]"; return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]";
} }

View File

@ -31,16 +31,24 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper; import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException; 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.QemuImg;
import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgException;
import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.libvirt.LibvirtException; import org.libvirt.LibvirtException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
@ResourceWrapper(handles = CheckVolumeCommand.class) @ResourceWrapper(handles = CheckVolumeCommand.class)
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> { public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
@Override @Override
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
String result = null; String result = null;
@ -50,34 +58,76 @@ public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<Check
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid()); KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
try { try {
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem || if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile); final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
final String path = vol.getPath(); final String path = vol.getPath();
long size = getVirtualSizeFromFile(path); try {
return new CheckVolumeAnswer(command, "", size); 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 { } else {
return new Answer(command, false, "Unsupported Storage Pool"); return new Answer(command, false, "Unsupported Storage Pool");
} }
} catch (final Exception e) { } catch (final Exception e) {
logger.error("Error while locating disk: "+ e.getMessage()); logger.error("Error while checking the disk: {}", e.getMessage());
return new Answer(command, false, result); 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 { try {
QemuImg qemu = new QemuImg(0); QemuImg qemu = new QemuImg(0);
QemuImgFile qemuFile = new QemuImgFile(path); QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
Map<String, String> info = qemu.info(qemuFile); return qemu.info(qemuFile, secure);
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) { } 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;
}
} }

View File

@ -31,15 +31,22 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper; import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException; 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.QemuImg;
import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgException;
import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.libvirt.LibvirtException; import org.libvirt.LibvirtException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class) @ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> { public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
@Override @Override
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
@ -55,14 +62,19 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<
int timeoutInSecs = command.getWait(); int timeoutInSecs = command.getWait();
try { try {
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem || if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs); String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs);
logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename); logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename); final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
final String path = vol.getPath(); final String path = vol.getPath();
long size = getVirtualSizeFromFile(path); try {
return new CopyRemoteVolumeAnswer(command, "", filename, size); 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 { } else {
String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported"; String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported";
return new Answer(command, false, msg); return new Answer(command, false, msg);
@ -74,18 +86,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 { try {
QemuImg qemu = new QemuImg(0); QemuImg qemu = new QemuImg(0);
QemuImgFile qemuFile = new QemuImgFile(path); QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
Map<String, String> info = qemu.info(qemuFile); return qemu.info(qemuFile, secure);
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) { } 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;
}
} }

View File

@ -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.QemuImg.PhysicalDiskFormat;
import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgException;
import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.libvirt.LibvirtException; import org.libvirt.LibvirtException;
@ -91,37 +92,46 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
if (disk.getQemuEncryptFormat() != null) { if (disk.getQemuEncryptFormat() != null) {
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString()); 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); String fileFormat = info.get(QemuImg.FILE_FORMAT);
if (StringUtils.isNotBlank(fileFormat)) { if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
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()));
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);
} }
String encrypted = info.get(QemuImg.ENCRYPTED); addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
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));
volumes.add(volumeOnStorageTO); volumes.add(volumeOnStorageTO);
} }
return new GetVolumesOnStorageAnswer(command, volumes); 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) { private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand command, final KVMStoragePool storagePool, String keyword) {
List<VolumeOnStorageTO> volumes = new ArrayList<>(); List<VolumeOnStorageTO> volumes = new ArrayList<>();
@ -134,11 +144,21 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
if (!isDiskFormatSupported(disk)) { if (!isDiskFormatSupported(disk)) {
continue; 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(), VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, disk.getName(), disk.getName(), disk.getPath(),
disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize()); disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize());
if (disk.getQemuEncryptFormat() != null) { if (disk.getQemuEncryptFormat() != null) {
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString()); 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); volumes.add(volumeOnStorageTO);
} }
return new GetVolumesOnStorageAnswer(command, volumes); return new GetVolumesOnStorageAnswer(command, volumes);

View File

@ -25,7 +25,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor; import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor;
import org.apache.cloudstack.utils.cryptsetup.KeyFile; 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;
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import org.apache.cloudstack.utils.qemu.QemuImgException; 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.qemu.QemuObject;
import org.libvirt.Connect; import org.libvirt.Connect;
import org.libvirt.Domain; import org.libvirt.Domain;
@ -100,7 +98,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
newSize = ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(newSize); newSize = ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(newSize);
} else if (spool.getType().equals(StoragePoolType.PowerFlex)) { } 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) // 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) { if (pool.getType() != StoragePoolType.RBD && pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex) {
@ -214,21 +212,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) { private Answer handleMultipathSCSIResize(ResizeVolumeCommand command, KVMStoragePool pool) {
((MultipathSCSIPool)pool).resize(command.getPath(), command.getInstanceName(), command.getNewSize()); ((MultipathSCSIPool)pool).resize(command.getPath(), command.getInstanceName(), command.getNewSize());
return new ResizeVolumeAnswer(command, true, ""); return new ResizeVolumeAnswer(command, true, "");

View File

@ -16,13 +16,21 @@
// under the License. // under the License.
package com.cloud.hypervisor.kvm.storage; 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.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.qemu.QemuObject;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.libvirt.LibvirtException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
public class KVMPhysicalDisk { public class KVMPhysicalDisk {
private String path; private String path;
@ -71,6 +79,31 @@ public class KVMPhysicalDisk {
return hostIp; 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 PhysicalDiskFormat format;
private long size; private long size;
private long virtualSize; private long virtualSize;

View File

@ -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/), 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). 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] ## [2025-05-07]
### Added ### Added

View File

@ -622,7 +622,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
try { try {
templateProps.load(new FileInputStream(propFile.toFile())); templateProps.load(new FileInputStream(propFile.toFile()));
String desc = templateProps.getProperty("description"); String desc = templateProps.getProperty("description");
if (desc.startsWith("SystemVM Template")) { if (desc != null && desc.startsWith("SystemVM Template")) {
return true; return true;
} }
} catch (IOException e) { } catch (IOException e) {

View File

@ -74,12 +74,14 @@ import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeDetailVO;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotDetailsDao; import com.cloud.storage.dao.SnapshotDetailsDao;
import com.cloud.storage.dao.SnapshotDetailsVO; import com.cloud.storage.dao.SnapshotDetailsVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.storage.dao.VolumeDetailsDao;
@ -133,6 +135,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
ConfigurationDao _configDao; ConfigurationDao _configDao;
@Inject @Inject
private HostDao _hostDao; private HostDao _hostDao;
@Inject private VMTemplateDao _vmTemplateDao;
private long volumeStatsLastUpdate = 0L; private long volumeStatsLastUpdate = 0L;
private final Map<String, Pair<Long, Long>> volumeStats = new HashMap<>(); private final Map<String, Pair<Long, Long>> volumeStats = new HashMap<>();
@ -670,8 +673,15 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
storagePoolVO.getId(), csCloneId, null); storagePoolVO.getId(), csCloneId, null);
if (tmplPoolRef != 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 String rscName = LinstorUtil.RSC_PREFIX + volumeInfo.getUuid();
final DevelopersApi linstorApi = LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress()); final DevelopersApi linstorApi = LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress());
try { try {

View File

@ -29,6 +29,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; 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.collections.MapUtils;
import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.cloud.api.query.dao.AccountJoinDao; import com.cloud.api.query.dao.AccountJoinDao;
@ -4334,11 +4336,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
*/ */
private void buildSearchCriteriaForDomainAdmins(SearchCriteria<DataCenterJoinVO> sc, Account account) { private void buildSearchCriteriaForDomainAdmins(SearchCriteria<DataCenterJoinVO> sc, Account account) {
List<Long> domainIds = new ArrayList<>(); List<Long> domainIds = new ArrayList<>();
DomainVO domainRecord = _domainDao.findById(account.getDomainId()); DomainVO domainRecord = getDomainForAccount(account);
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());
}
logger.trace("adding caller's domain {} to the list of domains to search for zones", account.getDomainId()); logger.trace("adding caller's domain {} to the list of domains to search for zones", account.getDomainId());
domainIds.add(domainRecord.getId()); domainIds.add(domainRecord.getId());
// find all domain Ids till leaf // 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 * 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 * 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 // find all domain Id up to root domain for this account
List<Long> domainIds = new ArrayList<>(); List<Long> domainIds = new ArrayList<>();
DomainVO domainRecord = _domainDao.findById(account.getDomainId()); DomainVO domainRecord = getDomainForAccount(account);
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());
}
domainIds.add(domainRecord.getId()); domainIds.add(domainRecord.getId());
while (domainRecord.getParent() != null) { while (domainRecord.getParent() != null) {
domainRecord = _domainDao.findById(domainRecord.getParent()); domainRecord = _domainDao.findById(domainRecord.getParent());
@ -4495,6 +4499,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
boolean showRemovedTmpl = cmd.getShowRemoved(); boolean showRemovedTmpl = cmd.getShowRemoved();
Account caller = CallContext.current().getCallingAccount(); Account caller = CallContext.current().getCallingAccount();
Long parentTemplateId = cmd.getParentTemplateId(); Long parentTemplateId = cmd.getParentTemplateId();
Long domainId = cmd.getDomainId();
boolean isRecursive = cmd.isRecursive();
boolean listAll = false; boolean listAll = false;
if (templateFilter != null && templateFilter == TemplateFilter.all) { if (templateFilter != null && templateFilter == TemplateFilter.all) {
@ -4505,7 +4511,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
} }
List<Long> permittedAccountIds = new ArrayList<>(); List<Long> permittedAccountIds = new ArrayList<>();
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(domainId, isRecursive, null);
accountMgr.buildACLSearchParameters( accountMgr.buildACLSearchParameters(
caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds,
domainIdRecursiveListProject, listAll, false domainIdRecursiveListProject, listAll, false
@ -4533,7 +4539,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(),
cmd.getImageStoreId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller, cmd.getImageStoreId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller,
listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique(), listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique(),
templateType, isVnf, cmd.getArch()); templateType, isVnf, domainId, isRecursive, cmd.getArch());
} }
private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword,
@ -4542,7 +4548,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller, boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller,
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags, ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags,
boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, Boolean showUnique, String templateType, boolean showRemovedTmpl, List<Long> 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 // check if zone is configured, if not, just return empty list
List<HypervisorType> hypers = null; List<HypervisorType> hypers = null;
@ -4628,7 +4634,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
if (!permittedAccounts.isEmpty()) { if (!permittedAccounts.isEmpty()) {
domain = _domainDao.findById(permittedAccounts.get(0).getDomainId()); domain = _domainDao.findById(permittedAccounts.get(0).getDomainId());
} else { } else {
domain = _domainDao.findById(caller.getDomainId()); domain = _domainDao.findById(Objects.requireNonNullElse(domainId, caller.getDomainId()));
} }
setIdsListToSearchCriteria(sc, ids); setIdsListToSearchCriteria(sc, ids);
@ -4640,10 +4646,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
sc.addAnd("accountType", SearchCriteria.Op.EQ, Account.Type.PROJECT); 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) if ((templateFilter == TemplateFilter.self || templateFilter == TemplateFilter.selfexecutable)
&& (caller.getType() == Account.Type.DOMAIN_ADMIN || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN)) { && (accountMgr.isAdmin(caller.getAccountId()))) {
sc.addAnd("domainPath", SearchCriteria.Op.LIKE, domain.getPath() + "%"); if (isRecursive) {
sc.addAnd("domainPath", SearchCriteria.Op.LIKE, domain.getPath() + "%");
} else {
sc.addAnd("domainPath", SearchCriteria.Op.EQ, domain.getPath());
}
} }
List<Long> relatedDomainIds = new ArrayList<>(); List<Long> relatedDomainIds = new ArrayList<>();
@ -4949,6 +4959,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
Map<String, String> tags = cmd.getTags(); Map<String, String> tags = cmd.getTags();
boolean showRemovedISO = cmd.getShowRemoved(); boolean showRemovedISO = cmd.getShowRemoved();
Account caller = CallContext.current().getCallingAccount(); Account caller = CallContext.current().getCallingAccount();
Long domainId = cmd.getDomainId();
boolean isRecursive = cmd.isRecursive();
boolean listAll = false; boolean listAll = false;
if (isoFilter != null && isoFilter == TemplateFilter.all) { if (isoFilter != null && isoFilter == TemplateFilter.all) {
@ -4960,7 +4972,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
List<Long> permittedAccountIds = new ArrayList<>(); List<Long> permittedAccountIds = new ArrayList<>();
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(domainId, isRecursive, null);
accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false);
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
List<Account> permittedAccounts = new ArrayList<>(); List<Account> 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(), return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(),
cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(),
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, 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 @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_HARDWARE, Arrays.asList("cirrus", "vga", "qxl", "virtio"));
options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList()); options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList());
options.put(VmDetailConstants.IO_POLICY, Arrays.asList("threads", "native", "io_uring", "storage_specific")); 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_MULTIQUEUE_NUMBER, Collections.emptyList());
options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false")); options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false"));
options.put(VmDetailConstants.VIRTUAL_TPM_MODEL, Arrays.asList("tpm-tis", "tpm-crb")); 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(); Long domainId = cmd.getDomainId();
final Long projectId = cmd.getProjectId(); final Long projectId = cmd.getProjectId();
Boolean isRecursive = cmd.isRecursive(); Boolean isRecursive = cmd.isRecursive();
final Boolean listAll = cmd.listAll(); final boolean listAll = cmd.listAll();
final Long startIndex = cmd.getStartIndex(); final Long startIndex = cmd.getStartIndex();
final Long pageSize = cmd.getPageSizeVal(); final Long pageSize = cmd.getPageSizeVal();
final String keyword = cmd.getKeyword(); final String keyword = cmd.getKeyword();

View File

@ -476,6 +476,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
isoResponse.setExtractable(iso.isExtractable() && !(iso.getTemplateType() == TemplateType.PERHOST)); isoResponse.setExtractable(iso.isExtractable() && !(iso.getTemplateType() == TemplateType.PERHOST));
isoResponse.setCreated(iso.getCreatedOnStore()); isoResponse.setCreated(iso.getCreatedOnStore());
isoResponse.setDynamicallyScalable(iso.isDynamicallyScalable()); isoResponse.setDynamicallyScalable(iso.isDynamicallyScalable());
isoResponse.setFormat(iso.getFormat());
if (iso.getTemplateType() == TemplateType.PERHOST) { if (iso.getTemplateType() == TemplateType.PERHOST) {
// for TemplateManager.XS_TOOLS_ISO and TemplateManager.VMWARE_TOOLS_ISO, we didn't download, but is ready to use. // for TemplateManager.XS_TOOLS_ISO and TemplateManager.VMWARE_TOOLS_ISO, we didn't download, but is ready to use.
isoResponse.setReady(true); isoResponse.setReady(true);
@ -574,10 +575,14 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
isoResponse.setZoneName(iso.getDataCenterName()); isoResponse.setZoneName(iso.getDataCenterName());
} }
Long isoSize = iso.getSize(); long isoSize = iso.getSize();
if (isoSize > 0) { if (isoSize > 0) {
isoResponse.setSize(isoSize); isoResponse.setSize(isoSize);
} }
long isoPhysicalSize = iso.getPhysicalSize();
if (isoPhysicalSize > 0) {
isoResponse.setPhysicalSize(isoPhysicalSize);
}
if (iso.getUserDataId() != null) { if (iso.getUserDataId() != null) {
isoResponse.setUserDataId(iso.getUserDataUUid()); isoResponse.setUserDataId(iso.getUserDataUUid());

View File

@ -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_STATS_USER_ONLY, StatsCollector.vmStatsCollectUserVMOnly.value());
capabilities.put(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED, StatsCollector.vmDiskStatsRetentionEnabled.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.INSTANCES_DISKS_STATS_RETENTION_TIME, StatsCollector.vmDiskStatsMaxRetentionTime.value());
capabilities.put(ApiConstants.DYNAMIC_SCALING_ENABLED, UserVmManager.EnableDynamicallyScaleVm.value());
if (apiLimitEnabled) { if (apiLimitEnabled) {
capabilities.put("apiLimitInterval", apiLimitInterval); capabilities.put("apiLimitInterval", apiLimitInterval);
capabilities.put("apiLimitMax", apiLimitMax); capabilities.put("apiLimitMax", apiLimitMax);

View File

@ -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.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; 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.cloudstack.utils.volume.VirtualMachineDiskInfo;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; 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()); throw new CloudRuntimeException("Error while copying volume of remote instance: " + answer.getDetails());
} }
CopyRemoteVolumeAnswer copyRemoteVolumeAnswer = (CopyRemoteVolumeAnswer) answer; CopyRemoteVolumeAnswer copyRemoteVolumeAnswer = (CopyRemoteVolumeAnswer) answer;
if(!copyRemoteVolumeAnswer.getResult()) { checkVolume(copyRemoteVolumeAnswer.getVolumeDetails());
if (!copyRemoteVolumeAnswer.getResult()) {
throw new CloudRuntimeException("Unable to copy volume of remote instance"); throw new CloudRuntimeException("Unable to copy volume of remote instance");
} }
diskProfile.setSize(copyRemoteVolumeAnswer.getSize()); diskProfile.setSize(copyRemoteVolumeAnswer.getSize());
@ -2700,7 +2702,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new CloudRuntimeException("Disk not found or is invalid"); throw new CloudRuntimeException("Disk not found or is invalid");
} }
CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer; CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer;
if(!checkVolumeAnswer.getResult()) { try {
checkVolume(checkVolumeAnswer.getVolumeDetails());
} catch (CloudRuntimeException e) {
cleanupFailedImportVM(userVm);
throw e;
}
if (!checkVolumeAnswer.getResult()) {
cleanupFailedImportVM(userVm); cleanupFailedImportVM(userVm);
throw new CloudRuntimeException("Disk not found or is invalid"); throw new CloudRuntimeException("Disk not found or is invalid");
} }
@ -2726,6 +2734,31 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return userVm; 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 { private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean selectAny) throws InsufficientCapacityException, ResourceAllocationException {
NetworkVO defaultNetwork = null; NetworkVO defaultNetwork = null;

View File

@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import io.netty.util.IllegalReferenceCountException;
import org.apache.cloudstack.storage.template.UploadEntity; import org.apache.cloudstack.storage.template.UploadEntity;
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -230,8 +231,15 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
private void reset() { private void reset() {
request = null; request = null;
// destroy the decoder to release all resources // destroy the decoder to release all resources
decoder.destroy(); if (decoder != null) {
decoder = null; try {
decoder.destroy();
} catch (IllegalReferenceCountException e) {
logger.warn("Decoder already destroyed", e);
}
decoder = null;
}
} }
private HttpResponseStatus readFileUploadData() throws IOException { private HttpResponseStatus readFileUploadData() throws IOException {

View File

@ -953,9 +953,72 @@ class TestLinstorVolumes(cloudstackTestCase):
snapshot.delete(self.apiClient) snapshot.delete(self.apiClient)
@attr(tags=['basic'], required_hardware=False)
def test_10_create_template_from_snapshot(self):
"""
Create a template from a snapshot and start an instance from it
"""
self.virtual_machine.stop(self.apiClient)
volume = list_volumes(
self.apiClient,
virtualmachineid = self.virtual_machine.id,
type = "ROOT",
listall = True,
)
snapshot = Snapshot.create(
self.apiClient,
volume_id=volume[0].id,
account=self.account.name,
domainid=self.domain.id,
)
self.cleanup.append(snapshot)
self.assertIsNotNone(snapshot, "Could not create snapshot")
services = {
"displaytext": "IntegrationTestTemplate",
"name": "int-test-template",
"ostypeid": self.template.ostypeid,
"ispublic": "true"
}
custom_template = Template.create_from_snapshot(
self.apiClient,
snapshot,
services,
)
self.cleanup.append(custom_template)
# create VM from custom template
test_virtual_machine = VirtualMachine.create(
self.apiClient,
self.testdata[TestData.virtualMachine2],
accountid=self.account.name,
zoneid=self.zone.id,
serviceofferingid=self.compute_offering.id,
templateid=custom_template.id,
domainid=self.domain.id,
startvm=False,
mode='basic',
)
self.cleanup.append(test_virtual_machine)
TestLinstorVolumes._start_vm(test_virtual_machine)
test_virtual_machine.stop(self.apiClient)
test_virtual_machine.delete(self.apiClient, True)
self.cleanup.remove(test_virtual_machine)
custom_template.delete(self.apiClient)
self.cleanup.remove(custom_template)
snapshot.delete(self.apiClient)
self.cleanup.remove(snapshot)
@attr(tags=['advanced', 'migration'], required_hardware=False) @attr(tags=['advanced', 'migration'], required_hardware=False)
def test_10_migrate_volume_to_same_instance_pool(self): def test_11_migrate_volume_to_same_instance_pool(self):
"""Migrate volume to the same instance pool""" """Migrate volume to the same instance pool"""
if not self.testdata[TestData.migrationTests]: if not self.testdata[TestData.migrationTests]:
@ -1088,7 +1151,7 @@ class TestLinstorVolumes(cloudstackTestCase):
test_virtual_machine.delete(self.apiClient, True) test_virtual_machine.delete(self.apiClient, True)
@attr(tags=['advanced', 'migration'], required_hardware=False) @attr(tags=['advanced', 'migration'], required_hardware=False)
def test_11_migrate_volume_to_distinct_instance_pool(self): def test_12_migrate_volume_to_distinct_instance_pool(self):
"""Migrate volume to distinct instance pool""" """Migrate volume to distinct instance pool"""
if not self.testdata[TestData.migrationTests]: if not self.testdata[TestData.migrationTests]:
@ -1221,7 +1284,7 @@ class TestLinstorVolumes(cloudstackTestCase):
test_virtual_machine.delete(self.apiClient, True) test_virtual_machine.delete(self.apiClient, True)
@attr(tags=["basic"], required_hardware=False) @attr(tags=["basic"], required_hardware=False)
def test_12_create_vm_snapshots(self): def test_13_create_vm_snapshots(self):
"""Test to create VM snapshots """Test to create VM snapshots
""" """
vm = TestLinstorVolumes._start_vm(self.virtual_machine) vm = TestLinstorVolumes._start_vm(self.virtual_machine)
@ -1251,7 +1314,7 @@ class TestLinstorVolumes(cloudstackTestCase):
) )
@attr(tags=["basic"], required_hardware=False) @attr(tags=["basic"], required_hardware=False)
def test_13_revert_vm_snapshots(self): def test_14_revert_vm_snapshots(self):
"""Test to revert VM snapshots """Test to revert VM snapshots
""" """
@ -1313,7 +1376,7 @@ class TestLinstorVolumes(cloudstackTestCase):
) )
@attr(tags=["basic"], required_hardware=False) @attr(tags=["basic"], required_hardware=False)
def test_14_delete_vm_snapshots(self): def test_15_delete_vm_snapshots(self):
"""Test to delete vm snapshots """Test to delete vm snapshots
""" """

View File

@ -1241,6 +1241,9 @@ export default {
if (item.name === 'template') { if (item.name === 'template') {
query.templatefilter = 'self' query.templatefilter = 'self'
query.filter = 'self' query.filter = 'self'
} else if (item.name === 'iso') {
query.isofilter = 'self'
query.filter = 'self'
} }
if (item.param === 'account') { if (item.param === 'account') {

View File

@ -48,6 +48,11 @@ export default {
name: 'template', name: 'template',
title: 'label.templates', title: 'label.templates',
param: 'domainid' param: 'domainid'
},
{
name: 'iso',
title: 'label.isos',
param: 'domainid'
}], }],
tabs: [ tabs: [
{ {

View File

@ -979,8 +979,7 @@ export default {
keyboards: [], keyboards: [],
bootTypes: [], bootTypes: [],
bootModes: [], bootModes: [],
ioPolicyTypes: [], ioPolicyTypes: []
dynamicScalingVmConfig: false
}, },
rowCount: {}, rowCount: {},
loading: { loading: {
@ -1275,13 +1274,6 @@ export default {
type: 'Routing' type: 'Routing'
}, },
field: 'hostid' field: 'hostid'
},
dynamicScalingVmConfig: {
list: 'listConfigurations',
options: {
zoneid: _.get(this.zone, 'id'),
name: 'enable.dynamic.scale.vm'
}
} }
} }
}, },
@ -1424,7 +1416,7 @@ export default {
return Boolean('listUserData' in this.$store.getters.apis) return Boolean('listUserData' in this.$store.getters.apis)
}, },
dynamicScalingVmConfigValue () { dynamicScalingVmConfigValue () {
return this.options.dynamicScalingVmConfig?.[0]?.value === 'true' return this.$store.getters.features.dynamicscalingenabled
}, },
isCustomizedDiskIOPS () { isCustomizedDiskIOPS () {
return this.diskSelected?.iscustomizediops || false return this.diskSelected?.iscustomizediops || false
@ -2366,7 +2358,7 @@ export default {
param.loading = true param.loading = true
param.opts = [] param.opts = []
const options = param.options || {} 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 options.listall = true
} }
api(param.list, options).then((response) => { api(param.list, options).then((response) => {

View File

@ -957,8 +957,7 @@ export default {
keyboards: [], keyboards: [],
bootTypes: [], bootTypes: [],
bootModes: [], bootModes: [],
ioPolicyTypes: [], ioPolicyTypes: []
dynamicScalingVmConfig: false
}, },
rowCount: {}, rowCount: {},
loading: { loading: {
@ -1191,13 +1190,6 @@ export default {
type: 'Routing' type: 'Routing'
}, },
field: 'hostid' 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) return Boolean('listUserData' in this.$store.getters.apis)
}, },
dynamicScalingVmConfigValue () { dynamicScalingVmConfigValue () {
return this.options.dynamicScalingVmConfig?.[0]?.value === 'true' return this.$store.getters.features.dynamicscalingenabled
}, },
isCustomizedDiskIOPS () { isCustomizedDiskIOPS () {
return this.diskSelected?.iscustomizediops || false return this.diskSelected?.iscustomizediops || false
@ -2403,7 +2395,7 @@ export default {
param.loading = true param.loading = true
param.opts = [] param.opts = []
const options = param.options || {} 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 options.listall = true
} }
api(param.list, options).then((response) => { api(param.list, options).then((response) => {

View File

@ -152,7 +152,6 @@ export default {
template: {}, template: {},
userDataEnabled: false, userDataEnabled: false,
securityGroupsEnabled: false, securityGroupsEnabled: false,
dynamicScalingVmConfig: false,
loading: false, loading: false,
securitygroups: { securitygroups: {
loading: false, loading: false,
@ -197,7 +196,6 @@ export default {
this.fetchInstaceGroups() this.fetchInstaceGroups()
this.fetchServiceOfferingData() this.fetchServiceOfferingData()
this.fetchTemplateData() this.fetchTemplateData()
this.fetchDynamicScalingVmConfig()
this.fetchUserData() this.fetchUserData()
}, },
fetchZoneDetails () { fetchZoneDetails () {
@ -249,18 +247,8 @@ export default {
this.template = templateResponses[0] this.template = templateResponses[0]
}) })
}, },
fetchDynamicScalingVmConfig () { isDynamicScalingEnabled () {
const params = {} return this.template.isdynamicallyscalable && this.serviceOffering.dynamicscalingenabled && this.$store.getters.features.dynamicscalingenabled
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
}, },
fetchOsTypes () { fetchOsTypes () {
this.osTypes.loading = true this.osTypes.loading = true

View File

@ -41,7 +41,6 @@ import com.vmware.vim25.HostConnectInfo;
import com.vmware.vim25.HostFirewallInfo; import com.vmware.vim25.HostFirewallInfo;
import com.vmware.vim25.HostFirewallRuleset; import com.vmware.vim25.HostFirewallRuleset;
import com.vmware.vim25.HostHardwareSummary; import com.vmware.vim25.HostHardwareSummary;
import com.vmware.vim25.HostHyperThreadScheduleInfo;
import com.vmware.vim25.HostIpConfig; import com.vmware.vim25.HostIpConfig;
import com.vmware.vim25.HostIpRouteEntry; import com.vmware.vim25.HostIpRouteEntry;
import com.vmware.vim25.HostListSummaryQuickStats; import com.vmware.vim25.HostListSummaryQuickStats;
@ -94,10 +93,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
return _context.getVimClient().getDynamicProperty(_mor, "configManager"); return _context.getVimClient().getDynamicProperty(_mor, "configManager");
} }
public List<VirtualNicManagerNetConfig> getHostVirtualNicManagerNetConfig() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.virtualNicManagerInfo.netConfig");
}
public List<HostIpRouteEntry> getHostIpRouteEntries() throws Exception { public List<HostIpRouteEntry> getHostIpRouteEntries() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.network.routeTableInfo.ipRoute"); 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"); return _context.getVimClient().getDynamicProperty(_mor, "summary.quickStats");
} }
public HostHyperThreadScheduleInfo getHostHyperThreadInfo() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.hyperThread");
}
public HostNetworkInfo getHostNetworkInfo() throws Exception { public HostNetworkInfo getHostNetworkInfo() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.network"); return _context.getVimClient().getDynamicProperty(_mor, "config.network");
} }
@ -260,7 +251,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
@Override @Override
public ManagedObjectReference getHyperHostDatacenter() throws Exception { public ManagedObjectReference getHyperHostDatacenter() throws Exception {
Pair<DatacenterMO, String> dcPair = DatacenterMO.getOwnerDatacenter(getContext(), getMor()); Pair<DatacenterMO, String> dcPair = DatacenterMO.getOwnerDatacenter(getContext(), getMor());
assert (dcPair != null);
return dcPair.first().getMor(); return dcPair.first().getMor();
} }
@ -282,19 +272,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
throw new Exception("Standalone host is not supported"); throw new Exception("Standalone host is not supported");
} }
public ManagedObjectReference[] getHostLocalDatastore() throws Exception {
List<ManagedObjectReference> datastores = _context.getVimClient().getDynamicProperty(_mor, "datastore");
List<ManagedObjectReference> 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 { public HostVirtualSwitch getHostVirtualSwitchByName(String name) throws Exception {
List<HostVirtualSwitch> switches = _context.getVimClient().getDynamicProperty(_mor, "config.network.vswitch"); List<HostVirtualSwitch> switches = _context.getVimClient().getDynamicProperty(_mor, "config.network.vswitch");
@ -307,16 +284,17 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
return null; return null;
} }
public List<HostVirtualSwitch> getHostVirtualSwitch() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.network.vswitch");
}
public AboutInfo getHostAboutInfo() throws Exception { public AboutInfo getHostAboutInfo() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.product"); return _context.getVimClient().getDynamicProperty(_mor, "config.product");
} }
public VmwareHostType getHostType() throws Exception { public VmwareHostType getHostType() throws Exception {
AboutInfo aboutInfo = getHostAboutInfo(); 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())) if ("VMware ESXi".equals(aboutInfo.getName()))
return VmwareHostType.ESXi; return VmwareHostType.ESXi;
else if ("VMware ESX".equals(aboutInfo.getName())) 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()); 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<HostVirtualSwitch> 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<HostPortGroup> 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 { public HostPortGroupSpec getPortGroupSpec(String portGroupName) throws Exception {
HostNetworkInfo hostNetInfo = getHostNetworkInfo(); HostNetworkInfo hostNetInfo = getHostNetworkInfo();
List<HostPortGroup> portGroups = hostNetInfo.getPortgroup(); List<HostPortGroup> portGroups = hostNetInfo.getPortgroup();
@ -383,40 +317,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
return null; return null;
} }
public String getPortGroupNameByNicType(HostVirtualNicType nicType) throws Exception {
assert (nicType != null);
List<VirtualNicManagerNetConfig> netConfigs =
_context.getVimClient().getDynamicProperty(_mor, "config.virtualNicManagerInfo.netConfig");
if (netConfigs != null) {
for (VirtualNicManagerNetConfig netConfig : netConfigs) {
if (netConfig.getNicType().equals(nicType.toString())) {
List<HostVirtualNic> 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<HostVirtualNic> nics = netInfo.getConsoleVnic();
if (nics != null) {
for (HostVirtualNic nic : nics) {
return nic.getPortgroup();
}
}
}
return null;
}
public boolean hasOpaqueNSXNetwork() throws Exception{ public boolean hasOpaqueNSXNetwork() throws Exception{
HostNetworkInfo netInfo = getHostNetworkInfo(); HostNetworkInfo netInfo = getHostNetworkInfo();
List<HostOpaqueNetworkInfo> opaqueNetworks = netInfo.getOpaqueNetwork(); List<HostOpaqueNetworkInfo> opaqueNetworks = netInfo.getOpaqueNetwork();
@ -436,9 +336,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
public boolean hasPortGroup(HostVirtualSwitch vSwitch, String portGroupName) throws Exception { public boolean hasPortGroup(HostVirtualSwitch vSwitch, String portGroupName) throws Exception {
ManagedObjectReference morNetwork = getNetworkMor(portGroupName); ManagedObjectReference morNetwork = getNetworkMor(portGroupName);
if (morNetwork != null) return morNetwork != null;
return true;
return false;
} }
public void createPortGroup(HostVirtualSwitch vSwitch, String portGroupName, Integer vlanId, HostNetworkSecurityPolicy secPolicy, 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); spec.setName(portGroupName);
if (vlanId != null) if (vlanId != null)
spec.setVlanId(vlanId.intValue()); spec.setVlanId(vlanId);
HostNetworkPolicy policy = new HostNetworkPolicy(); HostNetworkPolicy policy = new HostNetworkPolicy();
if (secPolicy != null) if (secPolicy != null)
policy.setSecurity(secPolicy); policy.setSecurity(secPolicy);
@ -471,7 +369,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
spec.setName(portGroupName); spec.setName(portGroupName);
if (vlanId != null) if (vlanId != null)
spec.setVlanId(vlanId.intValue()); spec.setVlanId(vlanId);
HostNetworkPolicy policy = new HostNetworkPolicy(); HostNetworkPolicy policy = new HostNetworkPolicy();
if (secPolicy != null) if (secPolicy != null)
policy.setSecurity(secPolicy); policy.setSecurity(secPolicy);
@ -525,15 +423,8 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
return null; return null;
} }
public List<ManagedObjectReference> 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 { public String getHostName() throws Exception {
return (String)_context.getVimClient().getDynamicProperty(_mor, "name"); return _context.getVimClient().getDynamicProperty(_mor, "name");
} }
@Override @Override
@ -572,11 +463,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
String internalCSUserVMNamingPattern = "^[i][-][0-9]+[-][0-9]+[-]"; String internalCSUserVMNamingPattern = "^[i][-][0-9]+[-][0-9]+[-]";
Pattern p = Pattern.compile(internalCSUserVMNamingPattern); Pattern p = Pattern.compile(internalCSUserVMNamingPattern);
java.util.regex.Matcher m = p.matcher(vmInternalCSName); java.util.regex.Matcher m = p.matcher(vmInternalCSName);
if (m.find()) { return m.find();
return true;
} else {
return false;
}
} }
private void loadVmCache() throws Exception { private void loadVmCache() throws Exception {
@ -723,7 +610,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
PropertyFilterSpec pfSpec = new PropertyFilterSpec(); PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec); pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec); pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>(); List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec); pfSpecArr.add(pfSpec);
List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr); List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -756,7 +643,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
PropertyFilterSpec pfSpec = new PropertyFilterSpec(); PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec); pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec); pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>(); List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
pfSpecArr.add(pfSpec); pfSpecArr.add(pfSpec);
List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr); List<ObjectContent> properties = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
@ -799,21 +686,6 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
return dsList; 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 @Override
public void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, String configurationId) throws Exception { public void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, String configurationId) throws Exception {