This is an automated email from the ASF dual-hosted git repository. weizhou pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 45daa1ce591a43442c30cc6ec9a70124440063eb Merge: 19f79b1d94f 44aa08c02a3 Author: Wei Zhou <weiz...@apache.org> AuthorDate: Fri Apr 12 16:40:07 2024 +0200 Merge remote-tracking branch 'apache/4.19' .../java/com/cloud/storage/VolumeApiService.java | 2 + api/src/main/java/com/cloud/vm/UserVmService.java | 2 +- .../api/command/user/vm/RestoreVMCmd.java | 45 ++- .../java/com/cloud/vm/VirtualMachineManager.java | 2 +- .../com/cloud/vm/VirtualMachineManagerImpl.java | 18 +- .../src/main/java/com/cloud/vm/VmWorkRestore.java | 27 +- .../engine/orchestration/CloudOrchestrator.java | 7 +- .../engine/orchestration/VolumeOrchestrator.java | 65 ++--- .../com/cloud/storage/VolumeApiServiceImpl.java | 14 +- .../main/java/com/cloud/vm/UserVmManagerImpl.java | 124 +++++++-- .../cloudstack/vm/UnmanagedVMsManagerImpl.java | 11 +- .../java/com/cloud/vm/UserVmManagerImplTest.java | 26 +- ui/src/config/section/compute.js | 27 +- ui/src/views/compute/ReinstallVm.vue | 307 +++++++++++++++++++++ 14 files changed, 542 insertions(+), 135 deletions(-) diff --cc api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java index e1c4dd5f678,17c4e97eb3b..3839049eee5 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java @@@ -16,9 -16,12 +16,9 @@@ // under the License. package org.apache.cloudstack.api.command.user.vm; - import org.apache.cloudstack.api.ApiCommandResourceType; - + import com.cloud.vm.VmDetailConstants; -import org.apache.cloudstack.api.ApiCommandResourceType; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.SecurityChecker.AccessType; ++import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@@ -28,6 -31,6 +28,7 @@@ import org.apache.cloudstack.api.Parame import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; ++import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; diff --cc engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 4372741de66,243613907ff..9f743668cd5 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@@ -5691,8 -5610,20 +5691,8 @@@ public class VirtualMachineManagerImpl return workJob; } - protected void resourceCountIncrement (long accountId, Long cpu, Long memory) { - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm); - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.cpu, cpu); - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.memory, memory); - } - - protected void resourceCountDecrement (long accountId, Long cpu, Long memory) { - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.user_vm); - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.cpu, cpu); - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.memory, memory); - } - @Override - public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException { + public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final Map<String, String> details) throws ResourceUnavailableException, InsufficientCapacityException { final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { VmWorkJobVO placeHolder = null; @@@ -5722,14 -5653,14 +5722,14 @@@ } } - private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException { - logger.debug("Restoring vm " + vmId + " with new templateId " + newTemplateId); + private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final Map<String, String> details) throws ResourceUnavailableException, InsufficientCapacityException { - s_logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " details : " + details); ++ logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " details : " + details); final CallContext context = CallContext.current(); final Account account = context.getCallingAccount(); - return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId); + return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, rootDiskOfferingId, expunge, details); } - public Outcome<VirtualMachine> restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId) { + public Outcome<VirtualMachine> restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, Map<String, String> details) { String commandName = VmWorkRestore.class.getName(); Pair<VmWorkJobVO, Long> pendingWorkJob = retrievePendingWorkJob(vmId, commandName); diff --cc server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 97bfef9a95d,566fcb38fc9..d76713b73cb --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@@ -7825,20 -7697,38 +7848,38 @@@ public class UserVmManagerImpl extends ex.addProxyObject(String.valueOf(vmId), "vmId"); throw ex; } - _accountMgr.checkAccess(caller, null, true, vm); + DiskOffering diskOffering = rootDiskOfferingId != null ? validateAndGetDiskOffering(rootDiskOfferingId, vm, caller) : null; + VMTemplateVO template = _templateDao.findById(newTemplateId); + if (template.getSize() != null) { + String rootDiskSize = details.get(VmDetailConstants.ROOT_DISK_SIZE); + Long templateSize = template.getSize(); + if (StringUtils.isNumeric(rootDiskSize)) { + if (Long.parseLong(rootDiskSize) * GiB_TO_BYTES < templateSize) { + throw new InvalidParameterValueException(String.format("Root disk size [%s] is smaller than the template size [%s]", rootDiskSize, templateSize)); + } + } else if (diskOffering != null && diskOffering.getDiskSize() < templateSize) { + throw new InvalidParameterValueException(String.format("Disk size for selected offering [%s] is less than the template's size [%s]", diskOffering.getDiskSize(), templateSize)); + } + } + //check if there are any active snapshots on volumes associated with the VM - s_logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vmId); + logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vmId); if (checkStatusOfVolumeSnapshots(vmId, Volume.Type.ROOT)) { throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on ROOT volume, Re-install VM is not permitted, please try again later."); } - s_logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm with id " + vmId); + logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm with id " + vmId); - return restoreVMInternal(caller, vm, newTemplateId); + return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, expunge, details); } - public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException { - return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId); + public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map<String, String> details) throws InsufficientCapacityException, ResourceUnavailableException { + return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, rootDiskOfferingId, expunge, details); + } + + + public UserVm restoreVMInternal(Account caller, UserVmVO vm) throws InsufficientCapacityException, ResourceUnavailableException { + return restoreVMInternal(caller, vm, null, null, false, null); } private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List<VolumeVO> rootVols, UserVmVO vm) { diff --cc server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 07d2e9bd59f,303a9b08b1c..1292b9e230c --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@@ -1450,131 -1379,6 +1436,131 @@@ public class UserVmManagerImplTest vmSnapshots.add(vmSnapshot); when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } + + @Test + public void addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecifiedTestDetailsConstantIsNotNullDoNothing() { + int currentValue = 123; + + for (String detailsConstant : detailsConstants) { + userVmManagerImpl.addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(null, customParameters, detailsConstant, currentValue); + } + + Assert.assertEquals(customParameters.get(VmDetailConstants.MEMORY), "2048"); + Assert.assertEquals(customParameters.get(VmDetailConstants.CPU_NUMBER), "4"); + Assert.assertEquals(customParameters.get(VmDetailConstants.CPU_SPEED), "1000"); + } + + @Test + public void addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecifiedTestNewValueIsNotNullDoNothing() { + Map<String, String> details = new HashMap<>(); + int currentValue = 123; + + for (String detailsConstant : detailsConstants) { + userVmManagerImpl.addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(321, details, detailsConstant, currentValue); + } + + Assert.assertNull(details.get(VmDetailConstants.MEMORY)); + Assert.assertNull(details.get(VmDetailConstants.CPU_NUMBER)); + Assert.assertNull(details.get(VmDetailConstants.CPU_SPEED)); + } + + @Test + public void addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecifiedTestBothValuesAreNullKeepCurrentValue() { + Map<String, String> details = new HashMap<>(); + int currentValue = 123; + + for (String detailsConstant : detailsConstants) { + userVmManagerImpl.addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(null, details, detailsConstant, currentValue); + } + + Assert.assertEquals(details.get(VmDetailConstants.MEMORY), String.valueOf(currentValue)); + Assert.assertEquals(details.get(VmDetailConstants.CPU_NUMBER), String.valueOf(currentValue)); + Assert.assertEquals(details.get(VmDetailConstants.CPU_SPEED),String.valueOf(currentValue)); + } + + @Test + public void addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecifiedTestNeitherValueIsNullDoNothing() { + int currentValue = 123; + + for (String detailsConstant : detailsConstants) { + userVmManagerImpl.addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(321, customParameters, detailsConstant, currentValue); + } + + Assert.assertEquals(customParameters.get(VmDetailConstants.MEMORY), "2048"); + Assert.assertEquals(customParameters.get(VmDetailConstants.CPU_NUMBER), "4"); + Assert.assertEquals(customParameters.get(VmDetailConstants.CPU_SPEED),"1000"); + } + + @Test + public void updateInstanceDetailsMapWithCurrentValuesForAbsentDetailsTestAllConstantsAreUpdated() { + Mockito.doReturn(serviceOffering).when(_serviceOfferingDao).findById(Mockito.anyLong()); + Mockito.doReturn(1L).when(vmInstanceMock).getId(); + Mockito.doReturn(1L).when(vmInstanceMock).getServiceOfferingId(); + Mockito.doReturn(serviceOffering).when(_serviceOfferingDao).findByIdIncludingRemoved(Mockito.anyLong(), Mockito.anyLong()); + userVmManagerImpl.updateInstanceDetailsMapWithCurrentValuesForAbsentDetails(null, vmInstanceMock, 0l); + + Mockito.verify(userVmManagerImpl).addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(Mockito.any(), Mockito.any(), Mockito.eq(VmDetailConstants.CPU_SPEED), Mockito.any()); + Mockito.verify(userVmManagerImpl).addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(Mockito.any(), Mockito.any(), Mockito.eq(VmDetailConstants.MEMORY), Mockito.any()); + Mockito.verify(userVmManagerImpl).addCurrentDetailValueToInstanceDetailsMapIfNewValueWasNotSpecified(Mockito.any(), Mockito.any(), Mockito.eq(VmDetailConstants.CPU_NUMBER), Mockito.any()); + } + + @Test + public void testCheckVolumesLimits() { + userVmManagerImpl.resourceLimitService = resourceLimitMgr; + long diskOffId1 = 1L; + DiskOfferingVO diskOfferingVO1 = Mockito.mock(DiskOfferingVO.class); + Mockito.when(diskOfferingDao.findById(diskOffId1)).thenReturn(diskOfferingVO1); + Mockito.when(resourceLimitMgr.getResourceLimitStorageTags(diskOfferingVO1)).thenReturn(List.of("tag1", "tag2")); + long diskOffId2 = 2L; + DiskOfferingVO diskOfferingVO2 = Mockito.mock(DiskOfferingVO.class); + Mockito.when(diskOfferingDao.findById(diskOffId2)).thenReturn(diskOfferingVO2); + Mockito.when(resourceLimitMgr.getResourceLimitStorageTags(diskOfferingVO2)).thenReturn(List.of("tag2")); + long diskOffId3 = 3L; + DiskOfferingVO diskOfferingVO3 = Mockito.mock(DiskOfferingVO.class); + Mockito.when(diskOfferingDao.findById(diskOffId3)).thenReturn(diskOfferingVO3); + Mockito.when(resourceLimitMgr.getResourceLimitStorageTags(diskOfferingVO3)).thenReturn(new ArrayList<>()); + + VolumeVO vol1 = Mockito.mock(VolumeVO.class); + Mockito.when(vol1.getDiskOfferingId()).thenReturn(diskOffId1); + Mockito.when(vol1.getSize()).thenReturn(10L); + Mockito.when(vol1.isDisplay()).thenReturn(true); + VolumeVO undisplayedVolume = Mockito.mock(VolumeVO.class); // shouldn't be considered for limits + Mockito.when(undisplayedVolume.isDisplay()).thenReturn(false); + VolumeVO vol3 = Mockito.mock(VolumeVO.class); + Mockito.when(vol3.getDiskOfferingId()).thenReturn(diskOffId2); + Mockito.when(vol3.getSize()).thenReturn(30L); + Mockito.when(vol3.isDisplay()).thenReturn(true); + VolumeVO vol4 = Mockito.mock(VolumeVO.class); + Mockito.when(vol4.getDiskOfferingId()).thenReturn(diskOffId3); + Mockito.when(vol4.getSize()).thenReturn(40L); + Mockito.when(vol4.isDisplay()).thenReturn(true); + VolumeVO vol5 = Mockito.mock(VolumeVO.class); + Mockito.when(vol5.getDiskOfferingId()).thenReturn(diskOffId1); + Mockito.when(vol5.getSize()).thenReturn(50L); + Mockito.when(vol5.isDisplay()).thenReturn(true); + + List<VolumeVO> volumes = List.of(vol1, undisplayedVolume, vol3, vol4, vol5); + Long size = volumes.stream().filter(VolumeVO::isDisplay).mapToLong(VolumeVO::getSize).sum(); + try { + userVmManagerImpl.checkVolumesLimits(account, volumes); + Mockito.verify(resourceLimitMgr, Mockito.times(1)) + .checkResourceLimit(account, Resource.ResourceType.volume, 4); + Mockito.verify(resourceLimitMgr, Mockito.times(1)) + .checkResourceLimit(account, Resource.ResourceType.primary_storage, size); + Mockito.verify(resourceLimitMgr, Mockito.times(1)) + .checkResourceLimitWithTag(account, Resource.ResourceType.volume, "tag1", 2); + Mockito.verify(resourceLimitMgr, Mockito.times(1)) + .checkResourceLimitWithTag(account, Resource.ResourceType.volume, "tag2", 3); + Mockito.verify(resourceLimitMgr, Mockito.times(1)) + .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, "tag1", + vol1.getSize() + vol5.getSize()); + Mockito.verify(resourceLimitMgr, Mockito.times(1)) + .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, "tag2", + vol1.getSize() + vol3.getSize() + vol5.getSize()); + } catch (ResourceAllocationException e) { + Assert.fail(e.getMessage()); + } + } }