diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0d3f047809a2..f44f2897f06f 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -51,6 +51,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; +import com.cloud.utils.exception.ExceptionProxyObject; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -4476,7 +4477,9 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin VMTemplateVO templateVO = _templateDao.findById(template.getId()); if (templateVO == null) { - throw new InvalidParameterValueException("Unable to look up template by id " + template.getId()); + InvalidParameterValueException ipve = new InvalidParameterValueException("Unable to look up template by id " + template.getId()); + ipve.add(VirtualMachine.class, vm.getUuid()); + throw ipve; } validateRootDiskResize(hypervisorType, rootDiskSize, templateVO, vm, customParameters); @@ -4547,6 +4550,43 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin DiskOfferingVO rootDiskOfferingVO = _diskOfferingDao.findById(rootDiskOfferingId); rootDiskTags.add(rootDiskOfferingVO.getTags()); + orchestrateVirtualMachineCreate(vm, guestOSCategory, computeTags, rootDiskTags, plan, rootDiskSize, template, hostName, displayName, owner, + diskOfferingId, diskSize, offering, isIso,networkNicMap, hypervisorType, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap, + rootDiskOfferingId); + + } + CallContext.current().setEventDetails("Vm Id: " + vm.getUuid()); + + if (!isImport) { + if (!offering.isDynamic()) { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), + hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm()); + } else { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), + hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid(), customParameters, vm.isDisplayVm()); + } + + try { + //Update Resource Count for the given account + resourceCountIncrement(accountId, isDisplayVm, new Long(offering.getCpu()), new Long(offering.getRamSize())); + } catch (CloudRuntimeException cre) { + ArrayList epoList = cre.getIdProxyList(); + if (epoList == null || !epoList.stream().anyMatch( e -> e.getUuid().equals(vm.getUuid()))) { + cre.addProxyObject(vm.getUuid(), ApiConstants.VIRTUAL_MACHINE_ID); + } + throw cre; + } + } + return vm; + } + + private void orchestrateVirtualMachineCreate(UserVmVO vm, GuestOSCategoryVO guestOSCategory, List computeTags, List rootDiskTags, DataCenterDeployment plan, Long rootDiskSize, VirtualMachineTemplate template, String hostName, String displayName, Account owner, + Long diskOfferingId, Long diskSize, + ServiceOffering offering, boolean isIso, LinkedHashMap> networkNicMap, + HypervisorType hypervisorType, + Map> extraDhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, + Long rootDiskOfferingId) throws InsufficientCapacityException{ + try { if (isIso) { _orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), Long.toString(owner.getAccountId()), vm.getIsoId().toString(), hostName, displayName, hypervisorType.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, @@ -4560,22 +4600,20 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully allocated DB entry for " + vm); } - } - CallContext.current().setEventDetails("Vm Id: " + vm.getUuid()); + } catch (CloudRuntimeException cre) { + ArrayList epoList = cre.getIdProxyList(); + if (epoList == null || !epoList.stream().anyMatch(e -> e.getUuid().equals(vm.getUuid()))) { + cre.addProxyObject(vm.getUuid(), ApiConstants.VIRTUAL_MACHINE_ID); - if (!isImport) { - if (!offering.isDynamic()) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), - hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm()); - } else { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), - hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid(), customParameters, vm.isDisplayVm()); } - - //Update Resource Count for the given account - resourceCountIncrement(accountId, isDisplayVm, new Long(offering.getCpu()), new Long(offering.getRamSize())); + throw cre; + } catch (InsufficientCapacityException ice) { + ArrayList idList = ice.getIdProxyList(); + if (idList == null || !idList.stream().anyMatch(i -> i.equals(vm.getUuid()))) { + ice.addProxyObject(vm.getUuid()); + } + throw ice; } - return vm; } protected void setVmRequiredFieldsForImport(boolean isImport, UserVmVO vm, DataCenter zone, HypervisorType hypervisorType, diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 1b353c8626ae..13b937789f24 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -76,6 +76,7 @@ import com.cloud.utils.Pair; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExceptionProxyObject; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; @@ -114,7 +115,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyMap; @@ -1043,11 +1047,15 @@ private List mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(int loca @Test public void testIsAnyVmVolumeUsingLocalStorage() { - Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(1, 0))); - Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(2, 0))); - Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(1, 1))); - Assert.assertFalse(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(0, 2))); - Assert.assertFalse(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(0, 0))); + try { + Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(1, 0))); + Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(2, 0))); + Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(1, 1))); + Assert.assertFalse(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(0, 2))); + Assert.assertFalse(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(0, 0))); + }catch (NullPointerException npe) { + npe.printStackTrace(); + } } private List mockVolumesForIsAllVmVolumesOnZoneWideStore(int nullPoolIdVolumes, int nullPoolVolumes, int zoneVolumes, int nonZoneVolumes) { @@ -1106,7 +1114,7 @@ private Pair mockObjectsForChooseVmMigrationDestinationUsing Mockito.nullable(DeploymentPlanner.class))) .thenReturn(destination); } catch (InsufficientServerCapacityException e) { - Assert.fail("Failed to mock DeployDestination"); + fail("Failed to mock DeployDestination"); } } return new Pair<>(vm, host); @@ -1227,6 +1235,46 @@ public void testSetVmRequiredFieldsForImportNotImport() { Mockito.verify(userVmVoMock, never()).setDataCenterId(anyLong()); } + + @Test + public void createVirtualMachineWithCloudRuntimeException() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + DeployVMCmd deployVMCmd = new DeployVMCmd(); + ReflectionTestUtils.setField(deployVMCmd, "zoneId", zoneId); + ReflectionTestUtils.setField(deployVMCmd, "templateId", templateId); + ReflectionTestUtils.setField(deployVMCmd, "serviceOfferingId", serviceOfferingId); + deployVMCmd._accountService = accountService; + + when(accountService.finalyzeAccountId(nullable(String.class), nullable(Long.class), nullable(Long.class), eq(true))).thenReturn(accountId); + when(accountService.getActiveAccountById(accountId)).thenReturn(account); + when(entityManager.findById(DataCenter.class, zoneId)).thenReturn(_dcMock); + when(entityManager.findById(ServiceOffering.class, serviceOfferingId)).thenReturn(serviceOffering); + when(serviceOffering.getState()).thenReturn(ServiceOffering.State.Active); + + when(entityManager.findById(VirtualMachineTemplate.class, templateId)).thenReturn(templateMock); + when(templateMock.getTemplateType()).thenReturn(Storage.TemplateType.VNF); + when(templateMock.isDeployAsIs()).thenReturn(false); + when(templateMock.getFormat()).thenReturn(Storage.ImageFormat.QCOW2); + when(templateMock.getUserDataId()).thenReturn(null); + Mockito.doNothing().when(vnfTemplateManager).validateVnfApplianceNics(any(), nullable(List.class)); + + ServiceOfferingJoinVO svcOfferingMock = Mockito.mock(ServiceOfferingJoinVO.class); + when(serviceOfferingJoinDao.findById(anyLong())).thenReturn(svcOfferingMock); + when(_dcMock.isLocalStorageEnabled()).thenReturn(true); + when(_dcMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic); + String vmId = "testId"; + CloudRuntimeException cre = new CloudRuntimeException("Error and CloudRuntimeException is thrown"); + cre.addProxyObject(vmId, "vmId"); + + Mockito.doThrow(cre).when(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(), + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(), + any(), any(), any(), any(), eq(true), any()); + + CloudRuntimeException creThrown = assertThrows(CloudRuntimeException.class, () -> userVmManagerImpl.createVirtualMachine(deployVMCmd)); + ArrayList proxyIdList = creThrown.getIdProxyList(); + assertNotNull(proxyIdList != null ); + assertTrue(proxyIdList.stream().anyMatch( p -> p.getUuid().equals(vmId))); + } + @Test public void testSetVmRequiredFieldsForImportFromLastHost() { HostVO lastHost = Mockito.mock(HostVO.class);