From bc3d7c314bb61af7dfb822a644c3032ff8d5c0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Fri, 9 Jan 2026 05:17:44 -0300 Subject: [PATCH 1/4] Change the `value` parameter of the `updateConfiguration` API to be required (#10790) --- .../com/cloud/configuration/ConfigurationManagerImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 705e7dea158..e6abc21e7da 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -1031,6 +1031,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati category = config.getCategory(); } + if (value == null) { + throw new InvalidParameterValueException(String.format("The new value for the [%s] configuration must be given.", name)); + } + validateIpAddressRelatedConfigValues(name, value); validateConflictingConfigValue(name, value); @@ -1039,10 +1043,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new CloudRuntimeException("Only Root Admin is allowed to edit this configuration."); } - if (value == null) { - return _configDao.findByName(name); - } - ConfigKey.Scope scope = null; Long id = null; int paramCountCheck = 0; From 9a38e75abdffdc22568c57f4abcb7519b7933ea9 Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Fri, 9 Jan 2026 05:48:04 -0300 Subject: [PATCH 2/4] Fix Linstor shrink qcow2 volumes (#12387) --- .../src/main/java/com/cloud/storage/VolumeApiServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 0faf9f1e2c0..4f8b55d16fb 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -2420,7 +2420,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - if (volume != null && ImageFormat.QCOW2.equals(volume.getFormat()) && !Volume.State.Allocated.equals(volume.getState()) && !StoragePoolType.StorPool.equals(volume.getPoolType())) { + if (volume != null && ImageFormat.QCOW2.equals(volume.getFormat()) && !Volume.State.Allocated.equals(volume.getState()) && + !Arrays.asList(StoragePoolType.StorPool, StoragePoolType.Linstor).contains(volume.getPoolType())) { String message = "Unable to shrink volumes of type QCOW2"; logger.warn(message); throw new InvalidParameterValueException(message); From c91e84c6d8b1b440d8c6af49f7b8f728c406e701 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:00:24 +0530 Subject: [PATCH 3/4] Avoid double counting primary storage allocated capacity for storage pools having a parent (#12181) --- .../java/com/cloud/capacity/dao/CapacityDaoImpl.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java index 5e7eee4566c..7bd3df103b1 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -213,6 +213,8 @@ public class CapacityDaoImpl extends GenericDaoBase implements private static final String LEFT_JOIN_VM_TEMPLATE = "LEFT JOIN vm_template ON vm_template.id = vi.vm_template_id "; + private static final String STORAGE_POOLS_WITH_CHILDREN = "SELECT DISTINCT parent FROM storage_pool WHERE parent != 0 AND removed IS NULL"; + public CapacityDaoImpl() { _hostIdTypeSearch = createSearchBuilder(); _hostIdTypeSearch.and("hostId", _hostIdTypeSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); @@ -379,6 +381,11 @@ public class CapacityDaoImpl extends GenericDaoBase implements finalQuery.append(" AND capacity_type = ?"); resourceIdList.add(capacityType.longValue()); } + + // Exclude storage pools with children from capacity calculations to avoid double counting + finalQuery.append(" AND NOT (capacity.capacity_type = ").append(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED) + .append(" AND capacity.host_id IN (").append(STORAGE_POOLS_WITH_CHILDREN).append("))"); + if (CollectionUtils.isNotEmpty(hostIds)) { finalQuery.append(String.format(" AND capacity.host_id IN (%s)", StringUtils.join(hostIds, ","))); if (capacityType == null) { @@ -541,6 +548,10 @@ public class CapacityDaoImpl extends GenericDaoBase implements StringBuilder sql = new StringBuilder(LIST_CAPACITY_GROUP_BY_CAPACITY_PART1); List resourceIdList = new ArrayList(); + // Exclude storage pools with children from capacity calculations to avoid double counting + sql.append(" AND NOT (capacity.capacity_type = ").append(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED) + .append(" AND capacity.host_id IN (").append(STORAGE_POOLS_WITH_CHILDREN).append("))"); + if (zoneId != null) { sql.append(" AND capacity.data_center_id = ?"); resourceIdList.add(zoneId); From ef1aaa0551d3d372e3ac7295b7f87dc203212a80 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 9 Jan 2026 18:26:39 +0530 Subject: [PATCH 4/4] kvm: allow skip forcing disk controller (#11750) --- .../java/com/cloud/vm/VmDetailConstants.java | 3 + .../resource/LibvirtComputingResource.java | 52 +++++++++++---- .../LibvirtComputingResourceTest.java | 65 +++++++++++++++++-- .../com/cloud/api/query/QueryManagerImpl.java | 1 + 4 files changed, 106 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index a6c9b6eba16..3d0152b0e43 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -54,6 +54,9 @@ public interface VmDetailConstants { String NIC_MULTIQUEUE_NUMBER = "nic.multiqueue.number"; String NIC_PACKED_VIRTQUEUES_ENABLED = "nic.packed.virtqueues.enabled"; + // KVM specific, disk controllers + String KVM_SKIP_FORCE_DISK_CONTROLLER = "skip.force.disk.controller"; + // Mac OSX guest specific (internal) String SMC_PRESENT = "smc.present"; String FIRMWARE = "firmware"; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 87ea55ca766..b66a838a3a5 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -107,7 +107,6 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.HostVmStateReportEntry; @@ -188,8 +187,8 @@ import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.RouterPrivateIpStrategy; import com.cloud.network.Networks.TrafficType; import com.cloud.resource.AgentStatusUpdater; -import com.cloud.resource.ResourceStatusUpdater; import com.cloud.resource.RequestWrapper; +import com.cloud.resource.ResourceStatusUpdater; import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.JavaStorageLayer; @@ -3083,6 +3082,44 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return useBLOCKDiskType(physicalDisk) ? DiskDef.DiskType.BLOCK : DiskDef.DiskType.FILE; } + /** + * Defines the disk configuration for the default pool type based on the provided parameters. + * It determines the appropriate disk settings depending on whether the disk is a data disk, whether + * it's a Windows template, whether UEFI is enabled, and whether secure boot is active. + * + * @param disk The disk definition object that will be configured with the disk settings. + * @param volume The volume (disk) object, containing information about the type of disk. + * @param isWindowsTemplate Flag indicating whether the template is a Windows template. + * @param isUefiEnabled Flag indicating whether UEFI is enabled. + * @param isSecureBoot Flag indicating whether secure boot is enabled. + * @param physicalDisk The physical disk object that contains the path to the disk. + * @param devId The device ID for the disk. + * @param diskBusType The disk bus type to use if not skipping force disk controller. + * @param diskBusTypeData The disk bus type to use for data disks, if applicable. + * @param details A map of VM details containing additional configuration values, such as whether to skip force + * disk controller. + */ + protected void defineDiskForDefaultPoolType(DiskDef disk, DiskTO volume, boolean isWindowsTemplate, + boolean isUefiEnabled, boolean isSecureBoot, KVMPhysicalDisk physicalDisk, int devId, + DiskDef.DiskBus diskBusType, DiskDef.DiskBus diskBusTypeData, Map details) { + boolean skipForceDiskController = MapUtils.getBoolean(details, VmDetailConstants.KVM_SKIP_FORCE_DISK_CONTROLLER, + false); + if (skipForceDiskController) { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, Volume.Type.DATADISK.equals(volume.getType()) ? + diskBusTypeData : diskBusType, DiskDef.DiskFmtType.QCOW2); + return; + } + if (volume.getType() == Volume.Type.DATADISK && !(isWindowsTemplate && isUefiEnabled)) { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData, DiskDef.DiskFmtType.QCOW2); + } else { + if (isSecureBoot) { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, DiskDef.DiskFmtType.QCOW2, isWindowsTemplate); + } else { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.DiskFmtType.QCOW2); + } + } + } + public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException { final Map details = vmSpec.getDetails(); final List disks = Arrays.asList(vmSpec.getDisks()); @@ -3244,15 +3281,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv disk.setDiscard(DiscardType.UNMAP); } } else { - if (volume.getType() == Volume.Type.DATADISK && !(isWindowsTemplate && isUefiEnabled)) { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData, DiskDef.DiskFmtType.QCOW2); - } else { - if (isSecureBoot) { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, DiskDef.DiskFmtType.QCOW2, isWindowsTemplate); - } else { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.DiskFmtType.QCOW2); - } - } + defineDiskForDefaultPoolType(disk, volume, isWindowsTemplate, isUefiEnabled, isSecureBoot, + physicalDisk, devId, diskBusType, diskBusTypeData, details); } pool.customizeLibvirtDiskDef(disk); } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 88e0983b63e..ed163787b11 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -56,9 +56,6 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import com.cloud.utils.net.NetUtils; - -import com.cloud.vm.VmDetailConstants; import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; @@ -217,13 +214,15 @@ import com.cloud.storage.template.TemplateLocation; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; +import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.OutputInterpreter.OneLineParser; +import com.cloud.utils.script.Script; import com.cloud.utils.ssh.SshHelper; import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VmDetailConstants; @RunWith(MockitoJUnitRunner.class) public class LibvirtComputingResourceTest { @@ -240,6 +239,19 @@ public class LibvirtComputingResourceTest { Connect connMock; @Mock LibvirtDomainXMLParser parserMock; + @Mock + private DiskDef diskDef; + @Mock + private DiskTO volume; + @Mock + private KVMPhysicalDisk physicalDisk; + @Mock + private Map details; + + private static final String PHYSICAL_DISK_PATH = "/path/to/disk"; + private static final int DEV_ID = 1; + private static final DiskDef.DiskBus DISK_BUS_TYPE = DiskDef.DiskBus.VIRTIO; + private static final DiskDef.DiskBus DISK_BUS_TYPE_DATA = DiskDef.DiskBus.SCSI; @Spy private LibvirtComputingResource libvirtComputingResourceSpy = Mockito.spy(new LibvirtComputingResource()); @@ -6565,4 +6577,49 @@ public class LibvirtComputingResourceTest { assertEquals(LibvirtVMDef.TpmDef.TpmModel.CRB, tpmDef.getModel()); assertEquals(LibvirtVMDef.TpmDef.TpmVersion.V2_0, tpmDef.getVersion()); } + + @Test + public void defineDiskForDefaultPoolTypeSkipsForceDiskController() { + Map details = new HashMap<>(); + details.put(VmDetailConstants.KVM_SKIP_FORCE_DISK_CONTROLLER, "true"); + Mockito.when(volume.getType()).thenReturn(Volume.Type.DATADISK); + Mockito.when(physicalDisk.getPath()).thenReturn(PHYSICAL_DISK_PATH); + libvirtComputingResourceSpy.defineDiskForDefaultPoolType(diskDef, volume, false, false, false, physicalDisk, DEV_ID, DISK_BUS_TYPE, DISK_BUS_TYPE_DATA, details); + Mockito.verify(diskDef).defFileBasedDisk(PHYSICAL_DISK_PATH, DEV_ID, DISK_BUS_TYPE_DATA, DiskDef.DiskFmtType.QCOW2); + } + + @Test + public void defineDiskForDefaultPoolTypeUsesDiskBusTypeDataForDataDiskWithoutWindowsAndUefi() { + Map details = new HashMap<>(); + Mockito.when(volume.getType()).thenReturn(Volume.Type.DATADISK); + Mockito.when(physicalDisk.getPath()).thenReturn(PHYSICAL_DISK_PATH); + libvirtComputingResourceSpy.defineDiskForDefaultPoolType(diskDef, volume, false, false, false, physicalDisk, DEV_ID, DISK_BUS_TYPE, DISK_BUS_TYPE_DATA, details); + Mockito.verify(diskDef).defFileBasedDisk(PHYSICAL_DISK_PATH, DEV_ID, DISK_BUS_TYPE_DATA, DiskDef.DiskFmtType.QCOW2); + } + + @Test + public void defineDiskForDefaultPoolTypeUsesDiskBusTypeForRootDisk() { + Map details = new HashMap<>(); + Mockito.when(volume.getType()).thenReturn(Volume.Type.ROOT); + Mockito.when(physicalDisk.getPath()).thenReturn(PHYSICAL_DISK_PATH); + libvirtComputingResourceSpy.defineDiskForDefaultPoolType(diskDef, volume, false, false, false, physicalDisk, DEV_ID, DISK_BUS_TYPE, DISK_BUS_TYPE_DATA, details); + Mockito.verify(diskDef).defFileBasedDisk(PHYSICAL_DISK_PATH, DEV_ID, DISK_BUS_TYPE, DiskDef.DiskFmtType.QCOW2); + } + + @Test + public void defineDiskForDefaultPoolTypeUsesSecureBootConfiguration() { + Map details = new HashMap<>(); + Mockito.when(volume.getType()).thenReturn(Volume.Type.ROOT); + Mockito.when(physicalDisk.getPath()).thenReturn(PHYSICAL_DISK_PATH); + libvirtComputingResourceSpy.defineDiskForDefaultPoolType(diskDef, volume, true, true, true, physicalDisk, DEV_ID, DISK_BUS_TYPE, DISK_BUS_TYPE_DATA, details); + Mockito.verify(diskDef).defFileBasedDisk(PHYSICAL_DISK_PATH, DEV_ID, DiskDef.DiskFmtType.QCOW2, true); + } + + @Test + public void defineDiskForDefaultPoolTypeHandlesNullDetails() { + Mockito.when(volume.getType()).thenReturn(Volume.Type.DATADISK); + Mockito.when(physicalDisk.getPath()).thenReturn(PHYSICAL_DISK_PATH); + libvirtComputingResourceSpy.defineDiskForDefaultPoolType(diskDef, volume, false, false, false, physicalDisk, DEV_ID, DISK_BUS_TYPE, DISK_BUS_TYPE_DATA, null); + Mockito.verify(diskDef).defFileBasedDisk(PHYSICAL_DISK_PATH, DEV_ID, DISK_BUS_TYPE_DATA, DiskDef.DiskFmtType.QCOW2); + } } diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 8641eb7ffc3..5833ede550e 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -5072,6 +5072,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q options.put(VmDetailConstants.VIRTUAL_TPM_VERSION, Arrays.asList("1.2", "2.0")); options.put(VmDetailConstants.GUEST_CPU_MODE, Arrays.asList("custom", "host-model", "host-passthrough")); options.put(VmDetailConstants.GUEST_CPU_MODEL, Collections.emptyList()); + options.put(VmDetailConstants.KVM_SKIP_FORCE_DISK_CONTROLLER, Arrays.asList("true", "false")); } if (HypervisorType.VMware.equals(hypervisorType)) {