mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 01:32:18 +02:00
Merge branch '4.19' into 4.20
This commit is contained in:
commit
609efcc231
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,22 +17,33 @@
|
||||
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CheckVolumeAnswer extends Answer {
|
||||
|
||||
private long size;
|
||||
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
|
||||
|
||||
CheckVolumeAnswer() {
|
||||
}
|
||||
|
||||
public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) {
|
||||
super(cmd, true, details);
|
||||
public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size,
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
|
||||
super(cmd, success, details);
|
||||
this.size = size;
|
||||
this.volumeDetails = volumeDetails;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CheckVolumeAnswer [size=" + size + "]";
|
||||
}
|
||||
|
||||
@ -17,21 +17,28 @@
|
||||
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CopyRemoteVolumeAnswer extends Answer {
|
||||
|
||||
private String remoteIp;
|
||||
private String filename;
|
||||
|
||||
private long size;
|
||||
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
|
||||
|
||||
CopyRemoteVolumeAnswer() {
|
||||
}
|
||||
|
||||
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) {
|
||||
super(cmd, true, details);
|
||||
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size,
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
|
||||
super(cmd, success, details);
|
||||
this.remoteIp = cmd.getRemoteIp();
|
||||
this.filename = filename;
|
||||
this.size = size;
|
||||
this.volumeDetails = volumeDetails;
|
||||
}
|
||||
|
||||
public String getRemoteIp() {
|
||||
@ -54,6 +61,10 @@ public class CopyRemoteVolumeAnswer extends Answer {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]";
|
||||
}
|
||||
|
||||
@ -31,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<CheckVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
String result = null;
|
||||
@ -50,34 +58,76 @@ public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<Check
|
||||
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
|
||||
|
||||
try {
|
||||
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
||||
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
||||
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
|
||||
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
|
||||
final String path = vol.getPath();
|
||||
long size = getVirtualSizeFromFile(path);
|
||||
return new CheckVolumeAnswer(command, "", size);
|
||||
try {
|
||||
KVMPhysicalDisk.checkQcow2File(path);
|
||||
} catch (final CloudRuntimeException e) {
|
||||
return new CheckVolumeAnswer(command, false, "", 0, getVolumeDetails(pool, vol));
|
||||
}
|
||||
|
||||
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
|
||||
return new CheckVolumeAnswer(command, true, "", size, getVolumeDetails(pool, vol));
|
||||
} else {
|
||||
return new Answer(command, false, "Unsupported Storage Pool");
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error while locating disk: "+ e.getMessage());
|
||||
logger.error("Error while checking the disk: {}", e.getMessage());
|
||||
return new Answer(command, false, result);
|
||||
}
|
||||
}
|
||||
|
||||
private long getVirtualSizeFromFile(String path) {
|
||||
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, true);
|
||||
if (MapUtils.isEmpty(info)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
|
||||
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(pool, disk);
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
|
||||
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
|
||||
return new HashMap<>(); // unknown
|
||||
}
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
|
||||
return qemu.info(qemuFile, secure);
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
logger.error("Failed to get info of disk file: " + ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, false);
|
||||
return info == null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,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<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
@ -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<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, true);
|
||||
if (MapUtils.isEmpty(info)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
|
||||
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(pool, disk);
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
|
||||
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
|
||||
return new HashMap<>(); // unknown
|
||||
}
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
|
||||
return qemu.info(qemuFile, secure);
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
logger.error("Failed to get info of disk file: " + ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, false);
|
||||
return info == null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
@ -91,37 +92,46 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
|
||||
if (disk.getQemuEncryptFormat() != null) {
|
||||
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString());
|
||||
}
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
if (!fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
|
||||
return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat()));
|
||||
}
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
|
||||
return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat()));
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(storagePool, disk);
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
|
||||
|
||||
volumes.add(volumeOnStorageTO);
|
||||
}
|
||||
return new GetVolumesOnStorageAnswer(command, volumes);
|
||||
}
|
||||
|
||||
private void addDetailsToVolumeOnStorageTO(VolumeOnStorageTO volumeOnStorageTO, final Map<String, String> info, final KVMStoragePool storagePool, final KVMPhysicalDisk disk) {
|
||||
if (MapUtils.isEmpty(info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(storagePool, disk);
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
}
|
||||
|
||||
private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand command, final KVMStoragePool storagePool, String keyword) {
|
||||
List<VolumeOnStorageTO> volumes = new ArrayList<>();
|
||||
|
||||
@ -134,11 +144,21 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
|
||||
if (!isDiskFormatSupported(disk)) {
|
||||
continue;
|
||||
}
|
||||
Map<String, String> info = getDiskFileInfo(storagePool, disk, true);
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, disk.getName(), disk.getName(), disk.getPath(),
|
||||
disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize());
|
||||
if (disk.getQemuEncryptFormat() != null) {
|
||||
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString());
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
|
||||
continue;
|
||||
}
|
||||
addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
|
||||
|
||||
volumes.add(volumeOnStorageTO);
|
||||
}
|
||||
return new GetVolumesOnStorageAnswer(command, volumes);
|
||||
|
||||
@ -25,7 +25,6 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor;
|
||||
import org.apache.cloudstack.utils.cryptsetup.KeyFile;
|
||||
@ -33,7 +32,6 @@ import org.apache.cloudstack.utils.qemu.QemuImageOptions;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.cloudstack.utils.qemu.QemuObject;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.Domain;
|
||||
@ -100,7 +98,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
|
||||
newSize = ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(newSize);
|
||||
} else if (spool.getType().equals(StoragePoolType.PowerFlex)) {
|
||||
// PowerFlex RAW/LUKS is already resized, we just notify the domain based on new size (considering LUKS overhead)
|
||||
newSize = getVirtualSizeFromFile(path);
|
||||
newSize = KVMPhysicalDisk.getVirtualSizeFromFile(path);
|
||||
}
|
||||
|
||||
if (pool.getType() != StoragePoolType.RBD && pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex) {
|
||||
@ -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) {
|
||||
((MultipathSCSIPool)pool).resize(command.getPath(), command.getInstanceName(), command.getNewSize());
|
||||
return new ResizeVolumeAnswer(command, true, "");
|
||||
|
||||
@ -16,13 +16,21 @@
|
||||
// under the License.
|
||||
package com.cloud.hypervisor.kvm.storage;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.storage.formatinspector.Qcow2Inspector;
|
||||
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.cloudstack.utils.qemu.QemuObject;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class KVMPhysicalDisk {
|
||||
private String path;
|
||||
@ -71,6 +79,31 @@ public class KVMPhysicalDisk {
|
||||
return hostIp;
|
||||
}
|
||||
|
||||
public static long getVirtualSizeFromFile(String path) {
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkQcow2File(String path) {
|
||||
if (ImageStoreUtil.isCorrectExtension(path, "qcow2")) {
|
||||
try {
|
||||
Qcow2Inspector.validateQcow2File(path);
|
||||
} catch (RuntimeException e) {
|
||||
throw new CloudRuntimeException("The volume file at path " + path + " is not a valid QCOW2. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PhysicalDiskFormat format;
|
||||
private long size;
|
||||
private long virtualSize;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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<String, Pair<Long, Long>> 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 {
|
||||
|
||||
@ -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<DataCenterJoinVO> sc, Account account) {
|
||||
List<Long> 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<Long> 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<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
|
||||
@ -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<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,
|
||||
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags,
|
||||
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
|
||||
List<HypervisorType> 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<Long> relatedDomainIds = new ArrayList<>();
|
||||
@ -4949,6 +4959,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
Map<String, String> 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<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);
|
||||
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
||||
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(),
|
||||
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();
|
||||
|
||||
@ -476,6 +476,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
||||
isoResponse.setExtractable(iso.isExtractable() && !(iso.getTemplateType() == TemplateType.PERHOST));
|
||||
isoResponse.setCreated(iso.getCreatedOnStore());
|
||||
isoResponse.setDynamicallyScalable(iso.isDynamicallyScalable());
|
||||
isoResponse.setFormat(iso.getFormat());
|
||||
if (iso.getTemplateType() == TemplateType.PERHOST) {
|
||||
// for TemplateManager.XS_TOOLS_ISO and TemplateManager.VMWARE_TOOLS_ISO, we didn't download, but is ready to use.
|
||||
isoResponse.setReady(true);
|
||||
@ -574,10 +575,14 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
||||
isoResponse.setZoneName(iso.getDataCenterName());
|
||||
}
|
||||
|
||||
Long isoSize = iso.getSize();
|
||||
long isoSize = iso.getSize();
|
||||
if (isoSize > 0) {
|
||||
isoResponse.setSize(isoSize);
|
||||
}
|
||||
long isoPhysicalSize = iso.getPhysicalSize();
|
||||
if (isoPhysicalSize > 0) {
|
||||
isoResponse.setPhysicalSize(isoPhysicalSize);
|
||||
}
|
||||
|
||||
if (iso.getUserDataId() != null) {
|
||||
isoResponse.setUserDataId(iso.getUserDataUUid());
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<VolumeOnStorageTO.Detail, String> volumeDetails) {
|
||||
if (MapUtils.isEmpty(volumeDetails)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.IS_LOCKED)) {
|
||||
String isLocked = volumeDetails.get(VolumeOnStorageTO.Detail.IS_LOCKED);
|
||||
if (Boolean.parseBoolean(isLocked)) {
|
||||
logFailureAndThrowException("Locked volume cannot be imported or unmanaged.");
|
||||
}
|
||||
}
|
||||
if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.IS_ENCRYPTED)) {
|
||||
String isEncrypted = volumeDetails.get(VolumeOnStorageTO.Detail.IS_ENCRYPTED);
|
||||
if (Boolean.parseBoolean(isEncrypted)) {
|
||||
logFailureAndThrowException("Encrypted volume cannot be imported or unmanaged.");
|
||||
}
|
||||
}
|
||||
if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.BACKING_FILE)) {
|
||||
String backingFile = volumeDetails.get(VolumeOnStorageTO.Detail.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFile)) {
|
||||
logFailureAndThrowException("Volume with backing file cannot be imported or unmanaged.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean selectAny) throws InsufficientCapacityException, ResourceAllocationException {
|
||||
NetworkVO defaultNetwork = null;
|
||||
|
||||
|
||||
@ -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<HttpObj
|
||||
private void reset() {
|
||||
request = null;
|
||||
// destroy the decoder to release all resources
|
||||
decoder.destroy();
|
||||
decoder = null;
|
||||
if (decoder != null) {
|
||||
try {
|
||||
decoder.destroy();
|
||||
} catch (IllegalReferenceCountException e) {
|
||||
logger.warn("Decoder already destroyed", e);
|
||||
}
|
||||
|
||||
decoder = null;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpResponseStatus readFileUploadData() throws IOException {
|
||||
|
||||
@ -953,9 +953,72 @@ class TestLinstorVolumes(cloudstackTestCase):
|
||||
|
||||
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)
|
||||
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"""
|
||||
|
||||
if not self.testdata[TestData.migrationTests]:
|
||||
@ -1088,7 +1151,7 @@ class TestLinstorVolumes(cloudstackTestCase):
|
||||
test_virtual_machine.delete(self.apiClient, True)
|
||||
|
||||
@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"""
|
||||
|
||||
if not self.testdata[TestData.migrationTests]:
|
||||
@ -1221,7 +1284,7 @@ class TestLinstorVolumes(cloudstackTestCase):
|
||||
test_virtual_machine.delete(self.apiClient, True)
|
||||
|
||||
@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
|
||||
"""
|
||||
vm = TestLinstorVolumes._start_vm(self.virtual_machine)
|
||||
@ -1251,7 +1314,7 @@ class TestLinstorVolumes(cloudstackTestCase):
|
||||
)
|
||||
|
||||
@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
|
||||
"""
|
||||
|
||||
@ -1313,7 +1376,7 @@ class TestLinstorVolumes(cloudstackTestCase):
|
||||
)
|
||||
|
||||
@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
|
||||
"""
|
||||
|
||||
|
||||
@ -1241,6 +1241,9 @@ export default {
|
||||
if (item.name === 'template') {
|
||||
query.templatefilter = 'self'
|
||||
query.filter = 'self'
|
||||
} else if (item.name === 'iso') {
|
||||
query.isofilter = 'self'
|
||||
query.filter = 'self'
|
||||
}
|
||||
|
||||
if (item.param === 'account') {
|
||||
|
||||
@ -48,6 +48,11 @@ export default {
|
||||
name: 'template',
|
||||
title: 'label.templates',
|
||||
param: 'domainid'
|
||||
},
|
||||
{
|
||||
name: 'iso',
|
||||
title: 'label.isos',
|
||||
param: 'domainid'
|
||||
}],
|
||||
tabs: [
|
||||
{
|
||||
|
||||
@ -979,8 +979,7 @@ export default {
|
||||
keyboards: [],
|
||||
bootTypes: [],
|
||||
bootModes: [],
|
||||
ioPolicyTypes: [],
|
||||
dynamicScalingVmConfig: false
|
||||
ioPolicyTypes: []
|
||||
},
|
||||
rowCount: {},
|
||||
loading: {
|
||||
@ -1275,13 +1274,6 @@ export default {
|
||||
type: 'Routing'
|
||||
},
|
||||
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)
|
||||
},
|
||||
dynamicScalingVmConfigValue () {
|
||||
return this.options.dynamicScalingVmConfig?.[0]?.value === 'true'
|
||||
return this.$store.getters.features.dynamicscalingenabled
|
||||
},
|
||||
isCustomizedDiskIOPS () {
|
||||
return this.diskSelected?.iscustomizediops || false
|
||||
@ -2366,7 +2358,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) => {
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<VirtualNicManagerNetConfig> getHostVirtualNicManagerNetConfig() throws Exception {
|
||||
return _context.getVimClient().getDynamicProperty(_mor, "config.virtualNicManagerInfo.netConfig");
|
||||
}
|
||||
|
||||
public List<HostIpRouteEntry> 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<DatacenterMO, String> 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<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 {
|
||||
List<HostVirtualSwitch> switches = _context.getVimClient().getDynamicProperty(_mor, "config.network.vswitch");
|
||||
|
||||
@ -307,16 +284,17 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<HostVirtualSwitch> 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<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 {
|
||||
HostNetworkInfo hostNetInfo = getHostNetworkInfo();
|
||||
List<HostPortGroup> 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<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{
|
||||
HostNetworkInfo netInfo = getHostNetworkInfo();
|
||||
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 {
|
||||
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<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 {
|
||||
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<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
|
||||
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
|
||||
pfSpecArr.add(pfSpec);
|
||||
|
||||
List<ObjectContent> 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<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
|
||||
List<PropertyFilterSpec> pfSpecArr = new ArrayList<>();
|
||||
pfSpecArr.add(pfSpec);
|
||||
|
||||
List<ObjectContent> 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 {
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user