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));
+    }
 }

Reply via email to