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