This is an automated email from the ASF dual-hosted git repository.

abhisar pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.20 by this push:
     new 2416db2a443 Fix NPE on external/unmanaged instance import using custom 
offerings (#12884)
2416db2a443 is described below

commit 2416db2a443932ee9ad924d7f3948c7c1d4c698e
Author: Fabricio Duarte <[email protected]>
AuthorDate: Fri Mar 27 01:55:16 2026 -0300

    Fix NPE on external/unmanaged instance import using custom offerings 
(#12884)
    
    * Fix NPE on external/unmanaged instance import using custom offerings
---
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     | 125 ++++++++++++++++-----
 .../cloudstack/vm/UnmanagedVMsManagerImplTest.java | 118 ++++++++++++++++++-
 2 files changed, 216 insertions(+), 27 deletions(-)

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 1b588b04210..d1265562deb 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -1352,10 +1352,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
         List<String> managedVms = new ArrayList<>(additionalNameFilters);
         managedVms.addAll(getHostsManagedVms(hosts));
 
-        List<String> resourceLimitHostTags = 
resourceLimitService.getResourceLimitHostTags(serviceOffering, template);
-        try (CheckedReservation vmReservation = new CheckedReservation(owner, 
Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, 
resourceLimitService);
-             CheckedReservation cpuReservation = new CheckedReservation(owner, 
Resource.ResourceType.cpu, resourceLimitHostTags, 
Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService);
-             CheckedReservation memReservation = new CheckedReservation(owner, 
Resource.ResourceType.memory, resourceLimitHostTags, 
Long.valueOf(serviceOffering.getRamSize()), reservationDao, 
resourceLimitService)) {
+        try {
 
         ActionEventUtils.onStartedActionEvent(userId, owner.getId(), 
EventTypes.EVENT_VM_IMPORT,
                 cmd.getEventDescription(), null, null, true, 0);
@@ -1497,7 +1494,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
                                                          String hostName, 
Account caller, Account owner, long userId,
                                                          ServiceOfferingVO 
serviceOffering, Map<String, Long> dataDiskOfferingMap,
                                                          Map<String, Long> 
nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
-                                                         Map<String, String> 
details, Boolean migrateAllowed, List<String> managedVms, boolean forced) {
+                                                         Map<String, String> 
details, Boolean migrateAllowed, List<String> managedVms, boolean forced) 
throws ResourceAllocationException {
         UserVm userVm = null;
         for (HostVO host : hosts) {
             HashMap<String, UnmanagedInstanceTO> unmanagedInstances = 
getUnmanagedInstancesForHost(host, instanceName, managedVms);
@@ -1541,11 +1538,18 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
 
                     template.setGuestOSId(guestOSHypervisor.getGuestOsId());
                 }
-                userVm = importVirtualMachineInternal(unmanagedInstance, 
instanceName, zone, cluster, host,
-                        template, displayName, hostName, 
CallContext.current().getCallingAccount(), owner, userId,
-                        serviceOffering, dataDiskOfferingMap,
-                        nicNetworkMap, nicIpAddressMap,
-                        details, migrateAllowed, forced, true);
+
+                List<Reserver> reservations = new ArrayList<>();
+                try {
+                    checkVmResourceLimitsForUnmanagedInstanceImport(owner, 
unmanagedInstance, serviceOffering, template, reservations);
+                    userVm = importVirtualMachineInternal(unmanagedInstance, 
instanceName, zone, cluster, host,
+                            template, displayName, hostName, 
CallContext.current().getCallingAccount(), owner, userId,
+                            serviceOffering, dataDiskOfferingMap,
+                            nicNetworkMap, nicIpAddressMap,
+                            details, migrateAllowed, forced, true);
+                } finally {
+                    ReservationHelper.closeAll(reservations);
+                }
                 break;
             }
             if (userVm != null) {
@@ -1555,6 +1559,36 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
         return userVm;
     }
 
+    protected void checkVmResourceLimitsForUnmanagedInstanceImport(Account 
owner, UnmanagedInstanceTO unmanagedInstance, ServiceOfferingVO 
serviceOffering, VMTemplateVO template, List<Reserver> reservations) throws 
ResourceAllocationException {
+        // When importing an unmanaged instance, the amount of CPUs and memory 
is obtained from the hypervisor unless powered off
+        // and not using a dynamic offering, unlike the external VM import 
that always obtains it from the compute offering
+        Integer cpu = serviceOffering.getCpu();
+        Integer memory = serviceOffering.getRamSize();
+
+        if (serviceOffering.isDynamic() || 
!UnmanagedInstanceTO.PowerState.PowerOff.equals(unmanagedInstance.getPowerState()))
 {
+            cpu = unmanagedInstance.getCpuCores();
+            memory = unmanagedInstance.getMemory();
+        }
+
+        if (cpu == null || cpu == 0) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("CPU cores [%s] is not valid for importing VM [%s].", cpu, 
unmanagedInstance.getName()));
+        }
+        if (memory == null || memory == 0) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Memory [%s] is not valid for importing VM [%s].", memory, 
unmanagedInstance.getName()));
+        }
+
+        List<String> resourceLimitHostTags = 
resourceLimitService.getResourceLimitHostTags(serviceOffering, template);
+
+        CheckedReservation vmReservation = new CheckedReservation(owner, 
Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, 
resourceLimitService);
+        reservations.add(vmReservation);
+
+        CheckedReservation cpuReservation = new CheckedReservation(owner, 
Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), 
reservationDao, resourceLimitService);
+        reservations.add(cpuReservation);
+
+        CheckedReservation memReservation = new CheckedReservation(owner, 
Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), 
reservationDao, resourceLimitService);
+        reservations.add(memReservation);
+    }
+
     private Pair<UnmanagedInstanceTO, Boolean> 
getSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String 
username,
                                                                  String 
password, String clusterName, String sourceHostName,
                                                                  String 
sourceVM) {
@@ -1582,7 +1616,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
                                                           Account caller, 
Account owner, long userId,
                                                           ServiceOfferingVO 
serviceOffering, Map<String, Long> dataDiskOfferingMap,
                                                           Map<String, Long> 
nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
-                                                          Map<String, String> 
details, ImportVmCmd cmd, boolean forced) {
+                                                          Map<String, String> 
details, ImportVmCmd cmd, boolean forced) throws ResourceAllocationException {
         Long existingVcenterId = cmd.getExistingVcenterId();
         String vcenter = cmd.getVcenter();
         String datacenterName = cmd.getDatacenterName();
@@ -1620,6 +1654,8 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
         UnmanagedInstanceTO sourceVMwareInstance = null;
         DataStoreTO temporaryConvertLocation = null;
         String ovfTemplateOnConvertLocation = null;
+        List<Reserver> reservations = new ArrayList<>();
+
         try {
             HostVO convertHost = 
selectKVMHostForConversionInCluster(destinationCluster, convertInstanceHostId);
             HostVO importHost = 
selectKVMHostForImportingInCluster(destinationCluster, importInstanceHostId);
@@ -1634,6 +1670,10 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
             Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = 
getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, 
clusterName, sourceHostName, sourceVMName);
             sourceVMwareInstance = sourceInstanceDetails.first();
             isClonedInstance = sourceInstanceDetails.second();
+
+            // Ensure that the configured resource limits will not be exceeded 
before beginning the conversion process
+            checkVmResourceLimitsForUnmanagedInstanceImport(owner, 
sourceVMwareInstance, serviceOffering, template, reservations);
+
             boolean isWindowsVm = 
sourceVMwareInstance.getOperatingSystem().toLowerCase().contains("windows");
             if (isWindowsVm) {
                 checkConversionSupportOnHost(convertHost, sourceVMName, true);
@@ -1681,6 +1721,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
             if (temporaryConvertLocation != null  && 
StringUtils.isNotBlank(ovfTemplateOnConvertLocation)) {
                 removeTemplate(temporaryConvertLocation, 
ovfTemplateOnConvertLocation);
             }
+            ReservationHelper.closeAll(reservations);
         }
     }
 
@@ -2400,10 +2441,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
 
         UserVm userVm = null;
 
-        List<String> resourceLimitHostTags = 
resourceLimitService.getResourceLimitHostTags(serviceOffering, template);
-        try (CheckedReservation vmReservation = new CheckedReservation(owner, 
Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, 
resourceLimitService);
-             CheckedReservation cpuReservation = new CheckedReservation(owner, 
Resource.ResourceType.cpu, resourceLimitHostTags, 
Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService);
-             CheckedReservation memReservation = new CheckedReservation(owner, 
Resource.ResourceType.memory, resourceLimitHostTags, 
Long.valueOf(serviceOffering.getRamSize()), reservationDao, 
resourceLimitService)) {
+        try {
 
         if (ImportSource.EXTERNAL == importSource) {
             String username = cmd.getUsername();
@@ -2466,6 +2504,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
 
         List<Reserver> reservations = new ArrayList<>();
         try {
+            checkVmResourceLimitsForExternalKvmVmImport(owner, 
serviceOffering, (VMTemplateVO) template, details, reservations);
             checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, 
dataDisks, diskOffering, dataDiskOfferingMap, reservations);
 
             // Check NICs and supplied networks
@@ -2630,24 +2669,20 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
         profiles.add(nicProfile);
         networkNicMap.put(network.getUuid(), profiles);
 
+        List<Reserver> reservations = new ArrayList<>();
         try {
+            checkVmResourceLimitsForExternalKvmVmImport(owner, 
serviceOffering, (VMTemplateVO) template, details, reservations);
             userVm = userVmManager.importVM(zone, null, template, null, 
displayName, owner,
                     null, caller, true, null, owner.getAccountId(), userId,
                     serviceOffering, null, hostName,
                     Hypervisor.HypervisorType.KVM, allDetails, powerState, 
networkNicMap);
-        } catch (InsufficientCapacityException ice) {
-            logger.error(String.format("Failed to import vm name: %s", 
instanceName), ice);
-            throw new 
ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage());
-        }
-        if (userVm == null) {
-            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to import vm name: %s", instanceName));
-        }
 
-        DiskOfferingVO diskOffering = 
diskOfferingDao.findById(serviceOffering.getDiskOfferingId());
+            if (userVm == null) {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to import vm name: %s", instanceName));
+            }
 
-        List<Reserver> reservations = new ArrayList<>();
-        List<String> resourceLimitStorageTags = 
resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, 
diskOffering);
-        try {
+            DiskOfferingVO diskOffering = 
diskOfferingDao.findById(serviceOffering.getDiskOfferingId());
+            List<String> resourceLimitStorageTags = 
resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, 
diskOffering);
             CheckedReservation volumeReservation = new 
CheckedReservation(owner, Resource.ResourceType.volume, 
resourceLimitStorageTags,
                     CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L 
: 0L, reservationDao, resourceLimitService);
             reservations.add(volumeReservation);
@@ -2720,6 +2755,9 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
             publishVMUsageUpdateResourceCount(userVm, dummyOffering, template);
             return userVm;
 
+        } catch (InsufficientCapacityException ice) { // This will be thrown 
by com.cloud.vm.UserVmService.importVM
+            logger.error(String.format("Failed to import vm name: %s", 
instanceName), ice);
+            throw new 
ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage());
         } catch (ResourceAllocationException e) {
             cleanupFailedImportVM(userVm);
             throw e;
@@ -2728,6 +2766,41 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
         }
     }
 
+    protected void checkVmResourceLimitsForExternalKvmVmImport(Account owner, 
ServiceOfferingVO serviceOffering, VMTemplateVO template, Map<String, String> 
details, List<Reserver> reservations) throws ResourceAllocationException {
+        // When importing an external VM, the amount of CPUs and memory is 
always obtained from the compute offering,
+        // unlike the unmanaged instance import that obtains it from the 
hypervisor unless the VM is powered off and the offering is fixed
+        Integer cpu = serviceOffering.getCpu();
+        Integer memory = serviceOffering.getRamSize();
+
+        if (serviceOffering.isDynamic()) {
+            cpu = getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details);
+            memory = getDetailAsInteger(VmDetailConstants.MEMORY, details);
+        }
+
+        List<String> resourceLimitHostTags = 
resourceLimitService.getResourceLimitHostTags(serviceOffering, template);
+
+        CheckedReservation vmReservation = new CheckedReservation(owner, 
Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, 
resourceLimitService);
+        reservations.add(vmReservation);
+
+        CheckedReservation cpuReservation = new CheckedReservation(owner, 
Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), 
reservationDao, resourceLimitService);
+        reservations.add(cpuReservation);
+
+        CheckedReservation memReservation = new CheckedReservation(owner, 
Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), 
reservationDao, resourceLimitService);
+        reservations.add(memReservation);
+    }
+
+    protected Integer getDetailAsInteger(String key, Map<String, String> 
details) {
+        String detail = details.get(key);
+        if (detail == null) {
+            throw new InvalidParameterValueException(String.format("Detail 
'%s' must be provided.", key));
+        }
+        try {
+            return Integer.valueOf(detail);
+        } catch (NumberFormatException e) {
+            throw new InvalidParameterValueException(String.format("Please 
provide a valid integer value for detail '%s'.", key));
+        }
+    }
+
     private void checkVolume(Map<VolumeOnStorageTO.Detail, String> 
volumeDetails) {
         if (MapUtils.isEmpty(volumeDetails)) {
             return;
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 d56299126a5..be513e8b717 100644
--- 
a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
+++ 
b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
@@ -37,8 +37,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.cloud.exception.ResourceAllocationException;
 import com.cloud.offering.DiskOffering;
 import com.cloud.resourcelimit.CheckedReservation;
+import com.cloud.vm.VmDetailConstants;
 import org.apache.cloudstack.api.ResponseGenerator;
 import org.apache.cloudstack.api.ResponseObject;
 import org.apache.cloudstack.api.ServerApiException;
@@ -55,6 +57,7 @@ import 
org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.resourcelimit.Reserver;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
@@ -72,6 +75,7 @@ import org.mockito.MockedConstruction;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnitRunner;
 
 import com.cloud.agent.AgentManager;
@@ -168,7 +172,8 @@ import com.cloud.vm.dao.VMInstanceDao;
 public class UnmanagedVMsManagerImplTest {
 
     @InjectMocks
-    private UnmanagedVMsManagerImpl unmanagedVMsManager = new 
UnmanagedVMsManagerImpl();
+    @Spy
+    private UnmanagedVMsManagerImpl unmanagedVMsManager;
 
     @Mock
     private UserVmManager userVmManager;
@@ -237,6 +242,14 @@ public class UnmanagedVMsManagerImplTest {
     EntityManager entityMgr;
     @Mock
     DeploymentPlanningManager deploymentPlanningManager;
+    @Mock
+    private Account accountMock;
+    @Mock
+    private ServiceOfferingVO serviceOfferingMock;
+    @Mock
+    private VMTemplateVO templateMock;
+    @Mock
+    private UnmanagedInstanceTO unmanagedInstanceMock;
 
     private static final long virtualMachineId = 1L;
 
@@ -363,6 +376,11 @@ public class UnmanagedVMsManagerImplTest {
 
         when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine);
         
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Running);
+
+        when(unmanagedInstanceMock.getCpuCores()).thenReturn(8);
+        when(unmanagedInstanceMock.getMemory()).thenReturn(4096);
+        when(serviceOfferingMock.getCpu()).thenReturn(4);
+        when(serviceOfferingMock.getRamSize()).thenReturn(2048);
     }
 
     @NotNull
@@ -1110,4 +1128,102 @@ public class UnmanagedVMsManagerImplTest {
 
         unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, 
hostId);
     }
+
+    @Test
+    public void 
checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromHypervisorWhenOfferingIsDynamic()
 throws Exception {
+        when(serviceOfferingMock.isDynamic()).thenReturn(true);
+        List<Reserver> reservations = new ArrayList<>();
+
+        try (MockedConstruction<CheckedReservation> mockedConstruction = 
Mockito.mockConstruction(CheckedReservation.class)) {
+            
unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock,
 unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations);
+
+            Assert.assertEquals(3, mockedConstruction.constructed().size());
+            Assert.assertEquals(3, reservations.size());
+            verify(unmanagedInstanceMock).getCpuCores();
+            verify(unmanagedInstanceMock).getMemory();
+        }
+    }
+
+    @Test
+    public void 
checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromHypervisorWhenVmIsPoweredOn()
 throws Exception {
+        
when(unmanagedInstanceMock.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOn);
+        when(serviceOfferingMock.isDynamic()).thenReturn(false);
+        List<Reserver> reservations = new ArrayList<>();
+
+        try (MockedConstruction<CheckedReservation> mockedConstruction = 
Mockito.mockConstruction(CheckedReservation.class)) {
+            
unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock,
 unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations);
+
+            Assert.assertEquals(3, mockedConstruction.constructed().size());
+            Assert.assertEquals(3, reservations.size());
+            verify(unmanagedInstanceMock).getCpuCores();
+            verify(unmanagedInstanceMock).getMemory();
+        }
+    }
+
+    @Test
+    public void 
checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromOfferingWhenOfferingIsNotDynamicAndVmIsPoweredOff()
 throws Exception {
+        
when(unmanagedInstanceMock.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOff);
+        when(serviceOfferingMock.isDynamic()).thenReturn(false);
+        List<Reserver> reservations = new ArrayList<>();
+
+        try (MockedConstruction<CheckedReservation> mockedConstruction = 
Mockito.mockConstruction(CheckedReservation.class)) {
+            
unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock,
 unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations);
+
+            Assert.assertEquals(3, mockedConstruction.constructed().size());
+            Assert.assertEquals(3, reservations.size());
+            verify(serviceOfferingMock).getCpu();
+            verify(serviceOfferingMock).getRamSize();
+            verify(unmanagedInstanceMock, Mockito.never()).getCpuCores();
+            verify(unmanagedInstanceMock, Mockito.never()).getMemory();
+        }
+    }
+
+    @Test
+    public void 
checkVmResourceLimitsForExternalKvmVmImportTestUsesInformationFromOfferingWhenOfferingIsNotDynamic()
 throws ResourceAllocationException {
+        when(serviceOfferingMock.isDynamic()).thenReturn(false);
+        Map<String, String> details = new HashMap<>();
+        List<Reserver> reservations = new ArrayList<>();
+
+        try (MockedConstruction<CheckedReservation> mockedConstruction = 
Mockito.mockConstruction(CheckedReservation.class)) {
+            
unmanagedVMsManager.checkVmResourceLimitsForExternalKvmVmImport(accountMock, 
serviceOfferingMock, templateMock, details, reservations);
+
+            Assert.assertEquals(3, mockedConstruction.constructed().size());
+            Assert.assertEquals(3, reservations.size());
+            verify(serviceOfferingMock).getCpu();
+            verify(serviceOfferingMock).getRamSize();
+            verify(unmanagedVMsManager, 
Mockito.never()).getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details);
+            verify(unmanagedVMsManager, 
Mockito.never()).getDetailAsInteger(VmDetailConstants.MEMORY, details);
+        }
+    }
+
+    @Test
+    public void 
checkVmResourceLimitsForExternalKvmVmImportTestUsesInformationFromDetailsWhenOfferingIsDynamic()
 throws ResourceAllocationException {
+        when(serviceOfferingMock.isDynamic()).thenReturn(true);
+        Map<String, String> details = new HashMap<>();
+        details.put(VmDetailConstants.CPU_NUMBER, "8");
+        details.put(VmDetailConstants.MEMORY, "4096");
+        List<Reserver> reservations = new ArrayList<>();
+
+        try (MockedConstruction<CheckedReservation> mockedConstruction = 
Mockito.mockConstruction(CheckedReservation.class)) {
+            
unmanagedVMsManager.checkVmResourceLimitsForExternalKvmVmImport(accountMock, 
serviceOfferingMock, templateMock, details, reservations);
+
+            Assert.assertEquals(3, mockedConstruction.constructed().size());
+            Assert.assertEquals(3, reservations.size());
+            
verify(unmanagedVMsManager).getDetailAsInteger(VmDetailConstants.CPU_NUMBER, 
details);
+            
verify(unmanagedVMsManager).getDetailAsInteger(VmDetailConstants.MEMORY, 
details);
+        }
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void 
getDetailAsIntegerTestThrowsInvalidParameterValueExceptionWhenDetailIsNull() {
+        Map<String, String> details = new HashMap<>();
+        unmanagedVMsManager.getDetailAsInteger("non-existent", details);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void 
getDetailAsIntegerTestThrowsInvalidParameterValueExceptionWhenValueIsInvalid() {
+        Map<String, String> details = new HashMap<>();
+        details.put("key", "not-a-number");
+        unmanagedVMsManager.getDetailAsInteger("key", details);
+    }
 }

Reply via email to