This is an automated email from the ASF dual-hosted git repository.
harikrishna pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new bfc4f60e1da [VMware to KVM migration] Check source VM against the
selected offering (#11908)
bfc4f60e1da is described below
commit bfc4f60e1da8f2c4e686d07a35b213b8cd659ec2
Author: Nicolas Vazquez <[email protected]>
AuthorDate: Sun Oct 26 08:15:26 2025 -0300
[VMware to KVM migration] Check source VM against the selected offering
(#11908)
* [VMware to KVM migration] Check source VM against the selected offering
* Fix build
---
.../java/com/cloud/hypervisor/guru/VMwareGuru.java | 29 +++++++++++++++
.../com/cloud/hypervisor/guru/VMwareGuruTest.java | 21 +++++++++++
.../cloudstack/vm/UnmanagedVMsManagerImpl.java | 35 ++++++++++++++++--
.../cloudstack/vm/UnmanagedVMsManagerImplTest.java | 42 ++++++++++++++++++++++
4 files changed, 124 insertions(+), 3 deletions(-)
diff --git
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index 88df637b0dd..14991e483df 100644
---
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -61,6 +61,7 @@ import
org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import com.cloud.agent.api.BackupSnapshotCommand;
@@ -1393,6 +1394,9 @@ public class VMwareGuru extends HypervisorGuruBase
implements HypervisorGuru, Co
String datacenter =
params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
String username =
params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
String password =
params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD);
+ Integer requestedCpuNumber =
params.containsKey(VmDetailConstants.CPU_NUMBER) ?
Integer.parseInt(params.get(VmDetailConstants.CPU_NUMBER)) : null;
+ Integer requestedCpuSpeed =
params.containsKey(VmDetailConstants.CPU_SPEED) ?
Integer.parseInt(params.get(VmDetailConstants.CPU_SPEED)) : null;
+ Integer requestedMemory = params.containsKey(VmDetailConstants.MEMORY)
? Integer.parseInt(params.get(VmDetailConstants.MEMORY)) : null;
try {
VmwareContext context = connectToVcenter(vcenter, username,
password);
@@ -1428,6 +1432,8 @@ public class VMwareGuru extends HypervisorGuruBase
implements HypervisorGuru, Co
}
}
+ checkSourceVmResourcesAgainstSelectedOfferingResources(vmMo,
requestedCpuNumber, requestedCpuSpeed, requestedMemory);
+
logger.debug(String.format("Cloning VM %s at VMware host %s on
vCenter %s", vmName, hostIp, vcenter));
VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo,
dataCenterMO);
logger.debug(String.format("VM %s cloned successfully, to VM %s",
vmName, clonedVM.getName()));
@@ -1444,6 +1450,29 @@ public class VMwareGuru extends HypervisorGuruBase
implements HypervisorGuru, Co
}
}
+ protected void
checkSourceVmResourcesAgainstSelectedOfferingResources(VirtualMachineMO vmMo,
Integer requestedCpuNumber, Integer requestedCpuSpeed, Integer requestedMemory)
throws Exception {
+ if (ObjectUtils.allNull(requestedCpuNumber, requestedCpuSpeed,
requestedMemory)) {
+ return;
+ }
+ VirtualMachineConfigSummary configSummary = vmMo.getConfigSummary();
+ if (configSummary != null) {
+ compareSourceVmResourceAgainstRequested(configSummary.getNumCpu(),
requestedCpuNumber, "CPU number");
+
compareSourceVmResourceAgainstRequested(configSummary.getCpuReservation(),
requestedCpuSpeed, "CPU speed");
+
compareSourceVmResourceAgainstRequested(configSummary.getMemorySizeMB(),
requestedMemory, "Memory");
+ }
+ }
+
+ protected void compareSourceVmResourceAgainstRequested(Integer
actualResource, Integer requestedResource, String resourceName) throws
Exception {
+ if (ObjectUtils.anyNull(actualResource, requestedResource)) {
+ return;
+ }
+ if (requestedResource < actualResource) {
+ String err = String.format("The requested %s (%d) is less than the
source VM %s (%d)", resourceName, requestedResource, resourceName,
actualResource);
+ logger.error(err);
+ throw new CloudRuntimeException(err);
+ }
+ }
+
private boolean isWindowsVm(VirtualMachineMO vmMo) throws Exception {
UnmanagedInstanceTO sourceInstance =
VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
return
sourceInstance.getOperatingSystem().toLowerCase().contains("windows");
diff --git
a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
index 6e96330ac58..270f61fcbbe 100644
---
a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
+++
b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
@@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import com.vmware.vim25.VirtualMachineConfigSummary;
import org.apache.cloudstack.storage.NfsMountManager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
@@ -626,4 +627,24 @@ public class VMwareGuruTest {
boolean result = vMwareGuru.removeVMTemplateOutOfBand(dataStore,
templateDir);
assertTrue(result);
}
+
+ @Test(expected = CloudRuntimeException.class)
+ public void
testCheckSourceVmResourcesAgainstSelectedOfferingResourcesInsufficientMemory()
throws Exception {
+ VirtualMachineMO virtualMachineMO =
Mockito.mock(VirtualMachineMO.class);
+ VirtualMachineConfigSummary configSummary =
Mockito.mock(VirtualMachineConfigSummary.class);
+
Mockito.when(virtualMachineMO.getConfigSummary()).thenReturn(configSummary);
+ Mockito.when(configSummary.getNumCpu()).thenReturn(1);
+ Mockito.when(configSummary.getMemorySizeMB()).thenReturn(2048);
+
vMwareGuru.checkSourceVmResourcesAgainstSelectedOfferingResources(virtualMachineMO,
1, 500, 1024);
+ }
+
+ @Test
+ public void
testCheckSourceVmResourcesAgainstSelectedOfferingResourcesGreaterOffering()
throws Exception {
+ VirtualMachineMO virtualMachineMO =
Mockito.mock(VirtualMachineMO.class);
+ VirtualMachineConfigSummary configSummary =
Mockito.mock(VirtualMachineConfigSummary.class);
+
Mockito.when(virtualMachineMO.getConfigSummary()).thenReturn(configSummary);
+ Mockito.when(configSummary.getNumCpu()).thenReturn(1);
+ Mockito.when(configSummary.getMemorySizeMB()).thenReturn(1024);
+
vMwareGuru.checkSourceVmResourcesAgainstSelectedOfferingResources(virtualMachineMO,
2, 1500, 2048);
+ }
}
diff --git
a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index 14e038487cd..13fa2608016 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -1644,16 +1644,45 @@ public class UnmanagedVMsManagerImpl implements
UnmanagedVMsManager {
}
private Pair<UnmanagedInstanceTO, Boolean>
getSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String
username,
- String
password, String clusterName, String sourceHostName,
- String
sourceVM) {
+
String password, String clusterName, String sourceHostName,
+
String sourceVM, ServiceOfferingVO serviceOffering) {
HypervisorGuru vmwareGuru =
hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
Map<String, String> params =
createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName,
username, password, clusterName, sourceHostName, sourceVM);
+ addServiceOfferingDetailsToParams(params, serviceOffering);
return
vmwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(sourceHostName, sourceVM,
params);
}
+ /**
+ * Add the minimum resources to check on the hypervisor source VM before
converting the instance against the selected offering resources
+ * @param params sets the minimum CPU number, CPU speed and memory to be
checked against the source VM
+ * @param serviceOffering service offering for the converted VM
+ */
+ protected void addServiceOfferingDetailsToParams(Map<String, String>
params, ServiceOfferingVO serviceOffering) {
+ if (serviceOffering != null) {
+ serviceOfferingDao.loadDetails(serviceOffering);
+ Map<String, String> serviceOfferingDetails =
serviceOffering.getDetails();
+
+ if (serviceOffering.getCpu() != null) {
+ params.put(VmDetailConstants.CPU_NUMBER,
String.valueOf(serviceOffering.getCpu()));
+ } else if (MapUtils.isNotEmpty(serviceOfferingDetails) &&
serviceOfferingDetails.containsKey(ApiConstants.MIN_CPU_NUMBER)) {
+ params.put(VmDetailConstants.CPU_NUMBER,
serviceOfferingDetails.get(ApiConstants.MIN_CPU_NUMBER));
+ }
+
+ if (serviceOffering.getSpeed() != null) {
+ params.put(VmDetailConstants.CPU_SPEED,
String.valueOf(serviceOffering.getSpeed()));
+ }
+
+ if (serviceOffering.getRamSize() != null) {
+ params.put(VmDetailConstants.MEMORY,
String.valueOf(serviceOffering.getRamSize()));
+ } else if (MapUtils.isNotEmpty(serviceOfferingDetails) &&
serviceOfferingDetails.containsKey(ApiConstants.MIN_MEMORY)) {
+ params.put(VmDetailConstants.MEMORY,
serviceOfferingDetails.get(ApiConstants.MIN_MEMORY));
+ }
+ }
+ }
+
private String createOvfTemplateOfSourceVmwareUnmanagedInstance(String
vcenter, String datacenterName, String username,
String
password, String clusterName, String sourceHostName,
String
sourceVMwareInstanceName, DataStoreTO convertLocation, int
threadsCountToExportOvf) {
@@ -1734,7 +1763,7 @@ public class UnmanagedVMsManagerImpl implements
UnmanagedVMsManager {
// sourceVMwareInstance could be a cloned instance from
sourceVMName, of the sourceVMName itself if its powered off.
// isClonedInstance indicates if the VM is a clone of sourceVMName
- Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails =
getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
clusterName, sourceHostName, sourceVMName);
+ Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails =
getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
clusterName, sourceHostName, sourceVMName, serviceOffering);
sourceVMwareInstance = sourceInstanceDetails.first();
isClonedInstance = sourceInstanceDetails.second();
diff --git
a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
index 60ce662b4f3..a24ba7f068b 100644
---
a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
+++
b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
@@ -40,6 +40,7 @@ import java.util.UUID;
import com.cloud.offering.DiskOffering;
import com.cloud.vm.ImportVMTaskVO;
+import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ResponseGenerator;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.ServerApiException;
@@ -1309,4 +1310,45 @@ public class UnmanagedVMsManagerImplTest {
Mockito.when(configKeyMockParamsAllowedList.value()).thenReturn("network,x");
unmanagedVMsManager.checkExtraParamsAllowed("--mac
00:0c:29:e6:3d:9d:ip:192.168.0.89,192.168.0.1,24,192.168.0.254 -x");
}
+
+ @Test
+ public void testAddServiceOfferingDetailsToParamsFixedOffering() {
+ Map<String, String> params = new HashMap<>();
+ ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
+ Mockito.when(serviceOfferingVO.getCpu()).thenReturn(2);
+ Mockito.when(serviceOfferingVO.getRamSize()).thenReturn(2048);
+ unmanagedVMsManager.addServiceOfferingDetailsToParams(params,
serviceOfferingVO);
+ Assert.assertEquals("2", params.get(VmDetailConstants.CPU_NUMBER));
+ Assert.assertEquals("2048", params.get(VmDetailConstants.MEMORY));
+ }
+
+ @Test
+ public void
testAddServiceOfferingDetailsToParamsCustomConstrainedOffering() {
+ Map<String, String> params = new HashMap<>();
+ ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
+ Map<String, String> details = new HashMap<>();
+ details.put(ApiConstants.MIN_CPU_NUMBER, "1");
+ details.put(ApiConstants.MIN_MEMORY, "1024");
+ Mockito.when(serviceOfferingVO.getDetails()).thenReturn(details);
+ Mockito.when(serviceOfferingVO.getCpu()).thenReturn(null);
+ Mockito.when(serviceOfferingVO.getSpeed()).thenReturn(1500);
+ Mockito.when(serviceOfferingVO.getRamSize()).thenReturn(null);
+ unmanagedVMsManager.addServiceOfferingDetailsToParams(params,
serviceOfferingVO);
+ Assert.assertEquals("1", params.get(VmDetailConstants.CPU_NUMBER));
+ Assert.assertEquals("1500", params.get(VmDetailConstants.CPU_SPEED));
+ Assert.assertEquals("1024", params.get(VmDetailConstants.MEMORY));
+ }
+
+ @Test
+ public void
testAddServiceOfferingDetailsToParamsCustomUnconstrainedOffering() {
+ Map<String, String> params = new HashMap<>();
+ ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
+ Mockito.when(serviceOfferingVO.getCpu()).thenReturn(null);
+ Mockito.when(serviceOfferingVO.getSpeed()).thenReturn(null);
+ Mockito.when(serviceOfferingVO.getRamSize()).thenReturn(null);
+ unmanagedVMsManager.addServiceOfferingDetailsToParams(params,
serviceOfferingVO);
+ Assert.assertFalse(params.containsKey(VmDetailConstants.CPU_NUMBER));
+ Assert.assertFalse(params.containsKey(VmDetailConstants.CPU_SPEED));
+ Assert.assertFalse(params.containsKey(VmDetailConstants.MEMORY));
+ }
}