From f8cf8a306754eeda707fc8cc0bcc3bc3c07791aa Mon Sep 17 00:00:00 2001 From: Vishesh Date: Mon, 18 Mar 2024 15:44:12 +0530 Subject: [PATCH 01/14] Allow overriding root diskoffering id & size while restoring VM --- .../main/java/com/cloud/vm/UserVmService.java | 2 +- .../api/command/user/vm/RestoreVMCmd.java | 21 ++++++++ .../com/cloud/vm/VirtualMachineManager.java | 2 +- .../cloud/vm/VirtualMachineManagerImpl.java | 18 +++---- .../main/java/com/cloud/vm/VmWorkRestore.java | 20 +++++--- .../java/com/cloud/vm/UserVmManagerImpl.java | 49 +++++++++++++++---- .../com/cloud/vm/UserVmManagerImplTest.java | 26 +++------- 7 files changed, 90 insertions(+), 48 deletions(-) diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index c32c099ed3a2..5b7342ad398b 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -492,7 +492,7 @@ UserVm moveVMToUser(AssignVMCmd moveUserVMCmd) throws ResourceAllocationExceptio UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; - UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException; + UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException; UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; diff --git 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 index 4b59bf560cb3..398255d55285 100644 --- 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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.vm; 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; @@ -60,6 +61,18 @@ public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd { description = "an optional template Id to restore vm from the new template. This can be an ISO id in case of restore vm deployed using ISO") private Long templateId; + @Parameter(name = ApiConstants.DISK_OFFERING_ID, + type = CommandType.UUID, + entityType = DiskOfferingResponse.class, + description = "Override root volume's diskoffering.", since = "4.19.1") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.ROOT_DISK_SIZE, + type = CommandType.LONG, + description = "Override root volume's size (in GB).", + since = "4.19.1") + private Long rootDiskSize; + @Override public String getEventType() { return EventTypes.EVENT_VM_RESTORE; @@ -112,6 +125,14 @@ public Long getId() { return getVmId(); } + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public Long getRootDiskSize() { + return rootDiskSize != null ? rootDiskSize * 1024L * 1024L * 1024L : null; + } + @Override public Long getApiResourceId() { return getId(); diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 8cd67f253310..d2b208c68b1f 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -254,7 +254,7 @@ static String getHypervisorHostname(String name) { */ boolean unmanage(String vmUuid); - UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException; + UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws ResourceUnavailableException, InsufficientCapacityException; boolean checkIfVmHasClusterWideVolumes(Long vmId); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 59d129bc065b..f95080b3a9bc 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5623,20 +5623,20 @@ protected void resourceCountDecrement (long accountId, Long cpu, Long memory) { } @Override - public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException { + public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws ResourceUnavailableException, InsufficientCapacityException { final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { VmWorkJobVO placeHolder = null; placeHolder = createPlaceHolderWork(vmId); try { - return orchestrateRestoreVirtualMachine(vmId, newTemplateId); + return orchestrateRestoreVirtualMachine(vmId, newTemplateId, diskOfferingId, rootDiskSize); } finally { if (placeHolder != null) { _workJobDao.expunge(placeHolder.getId()); } } } else { - final Outcome outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId); + final Outcome outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, diskOfferingId, rootDiskSize); retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), "restoreVirtualMachine"); @@ -5653,14 +5653,14 @@ public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId) t } } - private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException { - s_logger.debug("Restoring vm " + vmId + " with new templateId " + newTemplateId); + private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long diskOfferingId, final Long rootDiskSize) throws ResourceUnavailableException, InsufficientCapacityException { + s_logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + diskOfferingId + " rootDiskSize: " + rootDiskSize); final CallContext context = CallContext.current(); final Account account = context.getCallingAccount(); - return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId); + return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, diskOfferingId, rootDiskSize); } - public Outcome restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId) { + public Outcome restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long diskOfferingId, final Long rootDiskSize) { String commandName = VmWorkRestore.class.getName(); Pair pendingWorkJob = retrievePendingWorkJob(vmId, commandName); @@ -5670,7 +5670,7 @@ public Outcome restoreVirtualMachineThroughJobQueue(final long v Pair newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId); workJob = newVmWorkJobAndInfo.first(); - VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId); + VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, diskOfferingId, rootDiskSize); setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); } @@ -5682,7 +5682,7 @@ public Outcome restoreVirtualMachineThroughJobQueue(final long v @ReflectionUse private Pair orchestrateRestoreVirtualMachine(final VmWorkRestore work) throws Exception { VMInstanceVO vm = findVmById(work.getVmId()); - UserVm uservm = orchestrateRestoreVirtualMachine(vm.getId(), work.getTemplateId()); + UserVm uservm = orchestrateRestoreVirtualMachine(vm.getId(), work.getTemplateId(), work.getDiskOfferingId(), work.getRootDiskSize()); HashMap passwordMap = new HashMap<>(); passwordMap.put(uservm.getId(), uservm.getPassword()); return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap)); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java index cb3adae27aa3..5fa749a8dca2 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java @@ -20,19 +20,25 @@ public class VmWorkRestore extends VmWork { private static final long serialVersionUID = 195901782359759635L; private Long templateId; + private Long diskOfferingId; + private Long rootDiskSize; - public VmWorkRestore(long userId, long accountId, long vmId, String handlerName, Long templateId) { - super(userId, accountId, vmId, handlerName); - - this.templateId = templateId; - } - - public VmWorkRestore(VmWork vmWork, Long templateId) { + public VmWorkRestore(VmWork vmWork, Long templateId, Long diskOfferingId, Long rootDiskSize) { super(vmWork); this.templateId = templateId; + this.diskOfferingId = diskOfferingId; + this.rootDiskSize = rootDiskSize; } public Long getTemplateId() { return templateId; } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public Long getRootDiskSize() { + return rootDiskSize; + } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0d3f047809a2..192d06794253 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -566,6 +566,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject private VmStatsDao vmStatsDao; @Inject + private DataCenterDao dataCenterDao; + @Inject private MessageBus messageBus; @Inject protected CommandSetupHelper commandSetupHelper; @@ -3241,7 +3243,7 @@ public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityE ServiceOfferingVO offering = serviceOfferingDao.findById(vmInstance.getId(), serviceOfferingId); if (offering != null && offering.getRemoved() == null) { if (offering.isVolatileVm()) { - return restoreVMInternal(caller, vmInstance, null); + return restoreVMInternal(caller, vmInstance); } } else { throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId + " corresponding to the vm"); @@ -7642,6 +7644,12 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, long vmId = cmd.getVmId(); Long newTemplateId = cmd.getTemplateId(); + Long diskOfferingId = cmd.getDiskOfferingId(); + Long rootDiskSize = cmd.getRootDiskSize(); + + if (rootDiskSize != null && rootDiskSize < 0) { + throw new InvalidParameterValueException("Invalid disk size " + rootDiskSize); + } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { @@ -7649,20 +7657,34 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ex.addProxyObject(String.valueOf(vmId), "vmId"); throw ex; } - _accountMgr.checkAccess(caller, null, true, vm); + DiskOffering diskOffering; + if (diskOfferingId != null) { + diskOffering = _diskOfferingDao.findById(diskOfferingId); + if (diskOffering == null) { + throw new InvalidParameterValueException("Cannot find disk offering with ID " + diskOfferingId); + } + DataCenterVO zone = dataCenterDao.findById(vm.getDataCenterId()); + _accountMgr.checkAccess(caller, diskOffering, zone); + } + //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); 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); - return restoreVMInternal(caller, vm, newTemplateId); + return restoreVMInternal(caller, vm, newTemplateId, diskOfferingId, rootDiskSize); + } + + public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException { + return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, diskOfferingId, rootDiskSize); } - 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) throws InsufficientCapacityException, ResourceUnavailableException { + return restoreVMInternal(caller, vm, null, null, null); } private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List rootVols, UserVmVO vm) { @@ -7707,7 +7729,7 @@ private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTe } @Override - public UserVm restoreVirtualMachine(final Account caller, final long vmId, final Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException { + public UserVm restoreVirtualMachine(final Account caller, final long vmId, final Long newTemplateId, final Long diskOfferingId, final Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException { Long userId = caller.getId(); _userDao.findById(userId); UserVmVO vm = _vmDao.findById(vmId); @@ -7764,7 +7786,7 @@ public UserVm restoreVirtualMachine(final Account caller, final long vmId, final } } - List newVols = new ArrayList<>(); + DiskOffering diskOffering = diskOfferingId != null ? _diskOfferingDao.findById(diskOfferingId) : null; for (VolumeVO root : rootVols) { if ( !Volume.State.Allocated.equals(root.getState()) || newTemplateId != null ) { final UserVmVO userVm = vm; @@ -7797,15 +7819,22 @@ public Pair doInTransaction(final TransactionStatus status) th } else { newVol = volumeMgr.allocateDuplicateVolume(root, null); } - newVols.add(newVol); + + VolumeVO resizedVolume = (VolumeVO) newVol; if (userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !newVol.getSize().equals(template.getSize())) { - VolumeVO resizedVolume = (VolumeVO) newVol; if (template.getSize() != null) { resizedVolume.setSize(template.getSize()); - _volsDao.update(resizedVolume.getId(), resizedVolume); } } + if (diskOffering != null) { + resizedVolume.setDiskOfferingId(diskOffering.getId()); + resizedVolume.setSize(diskOffering.getDiskSize()); + } + if (rootDiskSize != null) { + resizedVolume.setSize(rootDiskSize); + } + _volsDao.update(resizedVolume.getId(), resizedVolume); // 1. Save usage event and update resource count for user vm volumes try { diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index e2c2b8ef9e26..1fda0e668b9b 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -48,8 +48,6 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.ScopeType; -import com.cloud.storage.Snapshot; -import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; @@ -1264,18 +1262,6 @@ public void testRestoreVMWithVolumeSnapshots() throws ResourceUnavailableExcepti when(cmd.getTemplateId()).thenReturn(2L); when(userVmDao.findById(vmId)).thenReturn(userVmVoMock); - List volumes = new ArrayList<>(); - long rootVolumeId = 1l; - VolumeVO rootVolumeOfVm = Mockito.mock(VolumeVO.class); - Mockito.when(rootVolumeOfVm.getId()).thenReturn(rootVolumeId); - volumes.add(rootVolumeOfVm); - when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(volumes); - - List snapshots = new ArrayList<>(); - SnapshotVO snapshot = Mockito.mock(SnapshotVO.class); - snapshots.add(snapshot); - when(snapshotDaoMock.listByStatus(rootVolumeId, Snapshot.State.Creating, Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp)).thenReturn(snapshots); - userVmManagerImpl.restoreVM(cmd); } @@ -1289,7 +1275,7 @@ public void testRestoreVirtualMachineNoOwner() throws ResourceUnavailableExcepti when(userVmVoMock.getAccountId()).thenReturn(accountId); when(accountDao.findById(accountId)).thenReturn(null); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); } @Test(expected = PermissionDeniedException.class) @@ -1303,7 +1289,7 @@ public void testRestoreVirtualMachineOwnerDisabled() throws ResourceUnavailableE when(accountDao.findById(accountId)).thenReturn(callerAccount); when(callerAccount.getState()).thenReturn(Account.State.DISABLED); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); } @Test(expected = CloudRuntimeException.class) @@ -1318,7 +1304,7 @@ public void testRestoreVirtualMachineNotInRightState() throws ResourceUnavailabl when(accountDao.findById(accountId)).thenReturn(callerAccount); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); } @Test(expected = InvalidParameterValueException.class) @@ -1339,7 +1325,7 @@ public void testRestoreVirtualMachineNoRootVolume() throws ResourceUnavailableEx when(templateDao.findById(currentTemplateId)).thenReturn(currentTemplate); when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(new ArrayList()); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); } @Test(expected = InvalidParameterValueException.class) @@ -1366,7 +1352,7 @@ public void testRestoreVirtualMachineMoreThanOneRootVolume() throws ResourceUnav volumes.add(rootVolume2); when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(volumes); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); } @Test(expected = InvalidParameterValueException.class) @@ -1393,6 +1379,6 @@ public void testRestoreVirtualMachineWithVMSnapshots() throws ResourceUnavailabl vmSnapshots.add(vmSnapshot); when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); } } From 2c1bee9f8ad9d319943590e85f430e780d6c0b0c Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 19 Mar 2024 00:14:23 +0530 Subject: [PATCH 02/14] UI changes --- ui/src/config/section/compute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index f1b3e1b2c995..3eb907e9a94e 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -164,7 +164,7 @@ export default { label: 'label.reinstall.vm', message: 'message.reinstall.vm', dataView: true, - args: ['virtualmachineid', 'templateid'], + args: ['virtualmachineid', 'templateid', 'diskofferingid', 'rootdisksize'], filters: (record) => { var filters = {} var filterParams = {} From 76e3990d4068116df1eadc7639f822d8818ca742 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 21 Mar 2024 13:25:25 +0530 Subject: [PATCH 03/14] Allow expunging of old disk while restoring a VM --- .../main/java/com/cloud/vm/UserVmService.java | 2 +- .../api/command/user/vm/RestoreVMCmd.java | 16 ++++++++-- .../com/cloud/vm/VirtualMachineManager.java | 2 +- .../cloud/vm/VirtualMachineManagerImpl.java | 18 +++++------ .../main/java/com/cloud/vm/VmWorkRestore.java | 17 +++++++--- .../java/com/cloud/vm/UserVmManagerImpl.java | 31 +++++++++++++------ .../com/cloud/vm/UserVmManagerImplTest.java | 12 +++---- ui/src/config/section/compute.js | 2 +- 8 files changed, 64 insertions(+), 36 deletions(-) diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index 5b7342ad398b..b4163406b57b 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -492,7 +492,7 @@ UserVm moveVMToUser(AssignVMCmd moveUserVMCmd) throws ResourceAllocationExceptio UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; - UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException; + UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException; UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; diff --git 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 index 398255d55285..807c6d11a208 100644 --- 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 @@ -65,7 +65,7 @@ public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd { type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "Override root volume's diskoffering.", since = "4.19.1") - private Long diskOfferingId; + private Long rootDiskOfferingId; @Parameter(name = ApiConstants.ROOT_DISK_SIZE, type = CommandType.LONG, @@ -73,6 +73,12 @@ public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd { since = "4.19.1") private Long rootDiskSize; + @Parameter(name = ApiConstants.EXPUNGE, + type = CommandType.BOOLEAN, + description = "Optional field to expunge old root volume after restore.", + since = "4.19.1") + private Boolean expungeRootDisk; + @Override public String getEventType() { return EventTypes.EVENT_VM_RESTORE; @@ -125,14 +131,18 @@ public Long getId() { return getVmId(); } - public Long getDiskOfferingId() { - return diskOfferingId; + public Long getRootDiskOfferingId() { + return rootDiskOfferingId; } public Long getRootDiskSize() { return rootDiskSize != null ? rootDiskSize * 1024L * 1024L * 1024L : null; } + public Boolean getExpungeRootDisk() { + return expungeRootDisk != null && expungeRootDisk; + } + @Override public Long getApiResourceId() { return getId(); diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index d2b208c68b1f..f7dbedaa12e0 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -254,7 +254,7 @@ static String getHypervisorHostname(String name) { */ boolean unmanage(String vmUuid); - UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws ResourceUnavailableException, InsufficientCapacityException; + UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException; boolean checkIfVmHasClusterWideVolumes(Long vmId); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index f95080b3a9bc..ac00964cbb49 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5623,20 +5623,20 @@ protected void resourceCountDecrement (long accountId, Long cpu, Long memory) { } @Override - public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws ResourceUnavailableException, InsufficientCapacityException { + public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException { final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { VmWorkJobVO placeHolder = null; placeHolder = createPlaceHolderWork(vmId); try { - return orchestrateRestoreVirtualMachine(vmId, newTemplateId, diskOfferingId, rootDiskSize); + return orchestrateRestoreVirtualMachine(vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); } finally { if (placeHolder != null) { _workJobDao.expunge(placeHolder.getId()); } } } else { - final Outcome outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, diskOfferingId, rootDiskSize); + final Outcome outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), "restoreVirtualMachine"); @@ -5653,14 +5653,14 @@ public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, L } } - private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long diskOfferingId, final Long rootDiskSize) throws ResourceUnavailableException, InsufficientCapacityException { - s_logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + diskOfferingId + " rootDiskSize: " + rootDiskSize); + private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException { + s_logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " rootDiskSize: " + rootDiskSize); final CallContext context = CallContext.current(); final Account account = context.getCallingAccount(); - return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, diskOfferingId, rootDiskSize); + return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); } - public Outcome restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long diskOfferingId, final Long rootDiskSize) { + public Outcome restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) { String commandName = VmWorkRestore.class.getName(); Pair pendingWorkJob = retrievePendingWorkJob(vmId, commandName); @@ -5670,7 +5670,7 @@ public Outcome restoreVirtualMachineThroughJobQueue(final long v Pair newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId); workJob = newVmWorkJobAndInfo.first(); - VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, diskOfferingId, rootDiskSize); + VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); } @@ -5682,7 +5682,7 @@ public Outcome restoreVirtualMachineThroughJobQueue(final long v @ReflectionUse private Pair orchestrateRestoreVirtualMachine(final VmWorkRestore work) throws Exception { VMInstanceVO vm = findVmById(work.getVmId()); - UserVm uservm = orchestrateRestoreVirtualMachine(vm.getId(), work.getTemplateId(), work.getDiskOfferingId(), work.getRootDiskSize()); + UserVm uservm = orchestrateRestoreVirtualMachine(vm.getId(), work.getTemplateId(), work.getRootDiskOfferingId(), work.getRootDiskSize(), work.getExpunge()); HashMap passwordMap = new HashMap<>(); passwordMap.put(uservm.getId(), uservm.getPassword()); return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap)); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java index 5fa749a8dca2..88ad10f8e79d 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java @@ -20,25 +20,32 @@ public class VmWorkRestore extends VmWork { private static final long serialVersionUID = 195901782359759635L; private Long templateId; - private Long diskOfferingId; + private Long rootDiskOfferingId; private Long rootDiskSize; - public VmWorkRestore(VmWork vmWork, Long templateId, Long diskOfferingId, Long rootDiskSize) { + private boolean expunge; + + public VmWorkRestore(VmWork vmWork, Long templateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) { super(vmWork); this.templateId = templateId; - this.diskOfferingId = diskOfferingId; + this.rootDiskOfferingId = rootDiskOfferingId; this.rootDiskSize = rootDiskSize; + this.expunge = expunge; } public Long getTemplateId() { return templateId; } - public Long getDiskOfferingId() { - return diskOfferingId; + public Long getRootDiskOfferingId() { + return rootDiskOfferingId; } public Long getRootDiskSize() { return rootDiskSize; } + + public boolean getExpunge() { + return expunge; + } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 192d06794253..f54110c0a3be 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7644,8 +7644,9 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, long vmId = cmd.getVmId(); Long newTemplateId = cmd.getTemplateId(); - Long diskOfferingId = cmd.getDiskOfferingId(); + Long rootDiskOfferingId = cmd.getRootDiskOfferingId(); Long rootDiskSize = cmd.getRootDiskSize(); + boolean expunge = cmd.getExpungeRootDisk(); if (rootDiskSize != null && rootDiskSize < 0) { throw new InvalidParameterValueException("Invalid disk size " + rootDiskSize); @@ -7660,10 +7661,10 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, _accountMgr.checkAccess(caller, null, true, vm); DiskOffering diskOffering; - if (diskOfferingId != null) { - diskOffering = _diskOfferingDao.findById(diskOfferingId); + if (rootDiskOfferingId != null) { + diskOffering = _diskOfferingDao.findById(rootDiskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException("Cannot find disk offering with ID " + diskOfferingId); + throw new InvalidParameterValueException("Cannot find disk offering with ID " + rootDiskOfferingId); } DataCenterVO zone = dataCenterDao.findById(vm.getDataCenterId()); _accountMgr.checkAccess(caller, diskOffering, zone); @@ -7675,16 +7676,16 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, 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); - return restoreVMInternal(caller, vm, newTemplateId, diskOfferingId, rootDiskSize); + return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); } - public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long diskOfferingId, Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException { - return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, diskOfferingId, rootDiskSize); + public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException { + return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); } public UserVm restoreVMInternal(Account caller, UserVmVO vm) throws InsufficientCapacityException, ResourceUnavailableException { - return restoreVMInternal(caller, vm, null, null, null); + return restoreVMInternal(caller, vm, null, null, null, false); } private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List rootVols, UserVmVO vm) { @@ -7729,7 +7730,9 @@ private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTe } @Override - public UserVm restoreVirtualMachine(final Account caller, final long vmId, final Long newTemplateId, final Long diskOfferingId, final Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException { + public UserVm restoreVirtualMachine(final Account caller, final long vmId, final Long newTemplateId, + final Long rootDiskOfferingId, final Long rootDiskSize, + final boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException { Long userId = caller.getId(); _userDao.findById(userId); UserVmVO vm = _vmDao.findById(vmId); @@ -7786,7 +7789,7 @@ public UserVm restoreVirtualMachine(final Account caller, final long vmId, final } } - DiskOffering diskOffering = diskOfferingId != null ? _diskOfferingDao.findById(diskOfferingId) : null; + DiskOffering diskOffering = rootDiskOfferingId != null ? _diskOfferingDao.findById(rootDiskOfferingId) : null; for (VolumeVO root : rootVols) { if ( !Volume.State.Allocated.equals(root.getState()) || newTemplateId != null ) { final UserVmVO userVm = vm; @@ -7865,6 +7868,14 @@ public Pair doInTransaction(final TransactionStatus status) th // Detach, destroy and create the usage event for the old root volume. _volsDao.detachVolume(root.getId()); volumeMgr.destroyVolume(root); + if (expunge) { + AsyncCallFuture future = _volService.expungeVolumeAsync(volFactory.getVolume(root.getId())); + try { + future.get(); + } catch (InterruptedException | java.util.concurrent.ExecutionException e) { + s_logger.error(String.format("Failed to expunge volume [uuid: %s name: %s] due to [%s].", root.getUuid(), root.getName(), e.getMessage()), e); + } + } // For VMware hypervisor since the old root volume is replaced by the new root volume, force expunge old root volume if it has been created in storage if (vm.getHypervisorType() == HypervisorType.VMware) { diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 1fda0e668b9b..6091720631b2 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -1275,7 +1275,7 @@ public void testRestoreVirtualMachineNoOwner() throws ResourceUnavailableExcepti when(userVmVoMock.getAccountId()).thenReturn(accountId); when(accountDao.findById(accountId)).thenReturn(null); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); } @Test(expected = PermissionDeniedException.class) @@ -1289,7 +1289,7 @@ public void testRestoreVirtualMachineOwnerDisabled() throws ResourceUnavailableE when(accountDao.findById(accountId)).thenReturn(callerAccount); when(callerAccount.getState()).thenReturn(Account.State.DISABLED); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); } @Test(expected = CloudRuntimeException.class) @@ -1304,7 +1304,7 @@ public void testRestoreVirtualMachineNotInRightState() throws ResourceUnavailabl when(accountDao.findById(accountId)).thenReturn(callerAccount); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); } @Test(expected = InvalidParameterValueException.class) @@ -1325,7 +1325,7 @@ public void testRestoreVirtualMachineNoRootVolume() throws ResourceUnavailableEx when(templateDao.findById(currentTemplateId)).thenReturn(currentTemplate); when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(new ArrayList()); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); } @Test(expected = InvalidParameterValueException.class) @@ -1352,7 +1352,7 @@ public void testRestoreVirtualMachineMoreThanOneRootVolume() throws ResourceUnav volumes.add(rootVolume2); when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(volumes); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); } @Test(expected = InvalidParameterValueException.class) @@ -1379,6 +1379,6 @@ public void testRestoreVirtualMachineWithVMSnapshots() throws ResourceUnavailabl vmSnapshots.add(vmSnapshot); when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); } } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 3eb907e9a94e..0c52107c87f9 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -164,7 +164,7 @@ export default { label: 'label.reinstall.vm', message: 'message.reinstall.vm', dataView: true, - args: ['virtualmachineid', 'templateid', 'diskofferingid', 'rootdisksize'], + args: ['virtualmachineid', 'templateid', 'diskofferingid', 'rootdisksize', 'expunge'], filters: (record) => { var filters = {} var filterParams = {} From ec0790cb81fb9a37d4f435ebb32796c6dac979bc Mon Sep 17 00:00:00 2001 From: Vishesh Date: Fri, 29 Mar 2024 01:22:06 +0530 Subject: [PATCH 04/14] Resolve comments --- .../main/java/com/cloud/vm/UserVmManagerImpl.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index f54110c0a3be..8fee5e9ebb91 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7668,6 +7668,10 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, } DataCenterVO zone = dataCenterDao.findById(vm.getDataCenterId()); _accountMgr.checkAccess(caller, diskOffering, zone); + ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId()); + if (serviceOffering.getDiskOfferingStrictness() && !serviceOffering.getDiskOfferingId().equals(rootDiskOfferingId)) { + throw new InvalidParameterValueException("VM's service offering has a strict disk offering requirement, and the specified disk offering does not match"); + } } //check if there are any active snapshots on volumes associated with the VM @@ -7867,15 +7871,7 @@ public Pair doInTransaction(final TransactionStatus status) th // Detach, destroy and create the usage event for the old root volume. _volsDao.detachVolume(root.getId()); - volumeMgr.destroyVolume(root); - if (expunge) { - AsyncCallFuture future = _volService.expungeVolumeAsync(volFactory.getVolume(root.getId())); - try { - future.get(); - } catch (InterruptedException | java.util.concurrent.ExecutionException e) { - s_logger.error(String.format("Failed to expunge volume [uuid: %s name: %s] due to [%s].", root.getUuid(), root.getName(), e.getMessage()), e); - } - } + _volumeService.destroyVolume(root.getId(), caller, expunge, false); // For VMware hypervisor since the old root volume is replaced by the new root volume, force expunge old root volume if it has been created in storage if (vm.getHypervisorType() == HypervisorType.VMware) { From 60cb8744c8745d4e49b1b1a73e856cc8b262d3f7 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 2 Apr 2024 14:06:35 +0530 Subject: [PATCH 05/14] Address comments --- .../java/com/cloud/vm/UserVmManagerImpl.java | 62 ++++++++++--------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 8fee5e9ebb91..443346827ad3 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7637,6 +7637,20 @@ private boolean canAccountUseNetwork(Account newAccount, Network network) { return false; } + private DiskOfferingVO validateAndGetDiskOffering(Long diskOfferingId, UserVmVO vm, Account caller) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); + if (diskOffering == null) { + throw new InvalidParameterValueException("Cannot find disk offering with ID " + diskOfferingId); + } + DataCenterVO zone = dataCenterDao.findById(vm.getDataCenterId()); + _accountMgr.checkAccess(caller, diskOffering, zone); + ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId()); + if (serviceOffering.getDiskOfferingStrictness() && !serviceOffering.getDiskOfferingId().equals(diskOfferingId)) { + throw new InvalidParameterValueException("VM's service offering has a strict disk offering requirement, and the specified disk offering does not match"); + } + return diskOffering; + } + @Override public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException { // Input validation @@ -7660,19 +7674,7 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, } _accountMgr.checkAccess(caller, null, true, vm); - DiskOffering diskOffering; - if (rootDiskOfferingId != null) { - diskOffering = _diskOfferingDao.findById(rootDiskOfferingId); - if (diskOffering == null) { - throw new InvalidParameterValueException("Cannot find disk offering with ID " + rootDiskOfferingId); - } - DataCenterVO zone = dataCenterDao.findById(vm.getDataCenterId()); - _accountMgr.checkAccess(caller, diskOffering, zone); - ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId()); - if (serviceOffering.getDiskOfferingStrictness() && !serviceOffering.getDiskOfferingId().equals(rootDiskOfferingId)) { - throw new InvalidParameterValueException("VM's service offering has a strict disk offering requirement, and the specified disk offering does not match"); - } - } + DiskOffering diskOffering = rootDiskOfferingId != null ? validateAndGetDiskOffering(rootDiskOfferingId, vm, caller) : null; //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); @@ -7827,21 +7829,7 @@ public Pair doInTransaction(final TransactionStatus status) th newVol = volumeMgr.allocateDuplicateVolume(root, null); } - VolumeVO resizedVolume = (VolumeVO) newVol; - - if (userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !newVol.getSize().equals(template.getSize())) { - if (template.getSize() != null) { - resizedVolume.setSize(template.getSize()); - } - } - if (diskOffering != null) { - resizedVolume.setDiskOfferingId(diskOffering.getId()); - resizedVolume.setSize(diskOffering.getDiskSize()); - } - if (rootDiskSize != null) { - resizedVolume.setSize(rootDiskSize); - } - _volsDao.update(resizedVolume.getId(), resizedVolume); + updateVolume(newVol, template, userVm, diskOffering, rootDiskSize); // 1. Save usage event and update resource count for user vm volumes try { @@ -7934,6 +7922,24 @@ public Pair doInTransaction(final TransactionStatus status) th } + private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Long rootDiskSize) { + VolumeVO resizedVolume = (VolumeVO) vol; + + if (userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !vol.getSize().equals(template.getSize())) { + if (template.getSize() != null) { + resizedVolume.setSize(template.getSize()); + } + } + if (diskOffering != null) { + resizedVolume.setDiskOfferingId(diskOffering.getId()); + resizedVolume.setSize(diskOffering.getDiskSize()); + } + if (rootDiskSize != null) { + resizedVolume.setSize(rootDiskSize); + } + _volsDao.update(resizedVolume.getId(), resizedVolume); + } + private void updateVMDynamicallyScalabilityUsingTemplate(UserVmVO vm, Long newTemplateId) { ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId()); VMTemplateVO newTemplate = _templateDao.findById(newTemplateId); From c1e5399580eec91025e110fb6e0c44d8dc6e9227 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 3 Apr 2024 00:41:26 +0530 Subject: [PATCH 06/14] Duplicate volume's details while duplicating volume --- .../engine/orchestration/VolumeOrchestrator.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 409b5388d72f..f80850dc267a 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -327,7 +327,19 @@ public VolumeVO allocateDuplicateVolumeVO(Volume oldVol, Long templateId) { newVol.setPassphraseId(passphrase.getId()); } - return _volsDao.persist(newVol); + VolumeVO volume = _volsDao.persist(newVol); + + // Duplicate volume's details + List oldVolDetails = _volDetailDao.listDetails(oldVol.getId()); + if (CollectionUtils.isNotEmpty(oldVolDetails)) { + List newVolDetails = new ArrayList<>(); + for (VolumeDetailVO oldVolDetail : oldVolDetails) { + VolumeDetailVO newVolDetail = new VolumeDetailVO(volume.getId(), oldVolDetail.getName(), oldVolDetail.getValue(), oldVolDetail.isDisplay()); + newVolDetails.add(newVolDetail); + } + _volDetailDao.saveDetails(newVolDetails); + } + return volume; } private Optional getMatchingStoragePool(String preferredPoolId, List storagePools) { From 0ce67ca6599bcabb58a68fedad0cd529af0595c6 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 3 Apr 2024 16:03:13 +0530 Subject: [PATCH 07/14] Allow setting IOPS for the new volume --- .../com/cloud/storage/VolumeApiService.java | 2 + .../main/java/com/cloud/vm/UserVmService.java | 2 +- .../api/command/user/vm/RestoreVMCmd.java | 17 +- .../com/cloud/vm/VirtualMachineManager.java | 2 +- .../service/VolumeOrchestrationService.java | 2 + .../cloud/vm/VirtualMachineManagerImpl.java | 18 +- .../main/java/com/cloud/vm/VmWorkRestore.java | 15 +- .../orchestration/VolumeOrchestrator.java | 43 ++- .../cloud/storage/VolumeApiServiceImpl.java | 14 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 45 ++- .../com/cloud/vm/UserVmManagerImplTest.java | 12 +- ui/src/config/section/compute.js | 27 +- ui/src/views/compute/ReinstallVm.vue | 297 ++++++++++++++++++ 13 files changed, 409 insertions(+), 87 deletions(-) create mode 100644 ui/src/views/compute/ReinstallVm.vue diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index a673df12d0f4..4f09702b7dba 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -175,6 +175,8 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc boolean validateVolumeSizeInBytes(long size); + void validateDestroyVolume(Volume volume, Account caller, boolean expunge, boolean forceExpunge); + Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException; void publishVolumeCreationUsageEvent(Volume volume); diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index b4163406b57b..787ed7bde37e 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -492,7 +492,7 @@ UserVm moveVMToUser(AssignVMCmd moveUserVMCmd) throws ResourceAllocationExceptio UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; - UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException; + UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map details) throws InsufficientCapacityException, ResourceUnavailableException; UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; diff --git 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 index 807c6d11a208..c2177c7451ae 100644 --- 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,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import com.cloud.vm.VmDetailConstants; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.log4j.Logger; @@ -43,6 +44,8 @@ import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; +import java.util.Map; + @APICommand(name = "restoreVirtualMachine", description = "Restore a VM to original template/ISO or new template/ISO", responseObject = UserVmResponse.class, since = "3.0.0", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) @@ -69,10 +72,14 @@ public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd { @Parameter(name = ApiConstants.ROOT_DISK_SIZE, type = CommandType.LONG, - description = "Override root volume's size (in GB).", + description = "Override root volume's size (in GB). Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", since = "4.19.1") private Long rootDiskSize; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.19.1", + description = "used to specify the custom parameters") + private Map details; + @Parameter(name = ApiConstants.EXPUNGE, type = CommandType.BOOLEAN, description = "Optional field to expunge old root volume after restore.", @@ -139,6 +146,14 @@ public Long getRootDiskSize() { return rootDiskSize != null ? rootDiskSize * 1024L * 1024L * 1024L : null; } + public Map getDetails() { + Map customparameterMap = convertDetailsToMap(details); + if (rootDiskSize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { + customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootDiskSize.toString()); + } + return customparameterMap; + } + public Boolean getExpungeRootDisk() { return expungeRootDisk != null && expungeRootDisk; } diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index f7dbedaa12e0..81125b22faf5 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -254,7 +254,7 @@ static String getHypervisorHostname(String name) { */ boolean unmanage(String vmUuid); - UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException; + UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge, Map details) throws ResourceUnavailableException, InsufficientCapacityException; boolean checkIfVmHasClusterWideVolumes(Long vmId); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index c4fbc2505aa4..8a9d5fed028f 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -130,6 +130,8 @@ DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Lon boolean canVmRestartOnAnotherServer(long vmId); + void saveVolumeDetails(Long diskOfferingId, Long volumeId); + /** * Allocate a volume or multiple volumes in case of template is registered with the 'deploy-as-is' option, allowing multiple disks */ diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index ac00964cbb49..a4173be35d3f 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5623,20 +5623,20 @@ protected void resourceCountDecrement (long accountId, Long cpu, Long memory) { } @Override - public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException { + public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge, final Map details) throws ResourceUnavailableException, InsufficientCapacityException { final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { VmWorkJobVO placeHolder = null; placeHolder = createPlaceHolderWork(vmId); try { - return orchestrateRestoreVirtualMachine(vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); + return orchestrateRestoreVirtualMachine(vmId, newTemplateId, rootDiskOfferingId, expunge, details); } finally { if (placeHolder != null) { _workJobDao.expunge(placeHolder.getId()); } } } else { - final Outcome outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); + final Outcome outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, rootDiskOfferingId, expunge, details); retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), "restoreVirtualMachine"); @@ -5653,14 +5653,14 @@ public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, f } } - private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException { - s_logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " rootDiskSize: " + rootDiskSize); + private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final Map details) throws ResourceUnavailableException, InsufficientCapacityException { + s_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, rootDiskOfferingId, rootDiskSize, expunge); + return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, rootDiskOfferingId, expunge, details); } - public Outcome restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) { + public Outcome restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, Map details) { String commandName = VmWorkRestore.class.getName(); Pair pendingWorkJob = retrievePendingWorkJob(vmId, commandName); @@ -5670,7 +5670,7 @@ public Outcome restoreVirtualMachineThroughJobQueue(final long v Pair newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId); workJob = newVmWorkJobAndInfo.first(); - VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); + VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, rootDiskOfferingId, expunge, details); setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); } @@ -5682,7 +5682,7 @@ public Outcome restoreVirtualMachineThroughJobQueue(final long v @ReflectionUse private Pair orchestrateRestoreVirtualMachine(final VmWorkRestore work) throws Exception { VMInstanceVO vm = findVmById(work.getVmId()); - UserVm uservm = orchestrateRestoreVirtualMachine(vm.getId(), work.getTemplateId(), work.getRootDiskOfferingId(), work.getRootDiskSize(), work.getExpunge()); + UserVm uservm = orchestrateRestoreVirtualMachine(vm.getId(), work.getTemplateId(), work.getRootDiskOfferingId(), work.getExpunge(), work.getDetails()); HashMap passwordMap = new HashMap<>(); passwordMap.put(uservm.getId(), uservm.getPassword()); return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap)); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java index 88ad10f8e79d..6a79bfe60f4a 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java @@ -16,21 +16,24 @@ // under the License. package com.cloud.vm; +import java.util.Map; + public class VmWorkRestore extends VmWork { private static final long serialVersionUID = 195901782359759635L; private Long templateId; private Long rootDiskOfferingId; private Long rootDiskSize; + private Map details; private boolean expunge; - public VmWorkRestore(VmWork vmWork, Long templateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) { + public VmWorkRestore(VmWork vmWork, Long templateId, Long rootDiskOfferingId, boolean expunge, Map details) { super(vmWork); this.templateId = templateId; this.rootDiskOfferingId = rootDiskOfferingId; - this.rootDiskSize = rootDiskSize; this.expunge = expunge; + this.details = details; } public Long getTemplateId() { @@ -41,11 +44,11 @@ public Long getRootDiskOfferingId() { return rootDiskOfferingId; } - public Long getRootDiskSize() { - return rootDiskSize; - } - public boolean getExpunge() { return expunge; } + + public Map getDetails() { + return details; + } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index f80850dc267a..3ee227131cb3 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -327,19 +327,7 @@ public VolumeVO allocateDuplicateVolumeVO(Volume oldVol, Long templateId) { newVol.setPassphraseId(passphrase.getId()); } - VolumeVO volume = _volsDao.persist(newVol); - - // Duplicate volume's details - List oldVolDetails = _volDetailDao.listDetails(oldVol.getId()); - if (CollectionUtils.isNotEmpty(oldVolDetails)) { - List newVolDetails = new ArrayList<>(); - for (VolumeDetailVO oldVolDetail : oldVolDetails) { - VolumeDetailVO newVolDetail = new VolumeDetailVO(volume.getId(), oldVolDetail.getName(), oldVolDetail.getValue(), oldVolDetail.isDisplay()); - newVolDetails.add(newVolDetail); - } - _volDetailDao.saveDetails(newVolDetails); - } - return volume; + return _volsDao.persist(newVol); } private Optional getMatchingStoragePool(String preferredPoolId, List storagePools) { @@ -946,18 +934,7 @@ private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering vol = _volsDao.persist(vol); - List volumeDetailsVO = new ArrayList(); - DiskOfferingDetailVO bandwidthLimitDetail = _diskOfferingDetailDao.findDetail(offering.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS); - if (bandwidthLimitDetail != null) { - volumeDetailsVO.add(new VolumeDetailVO(vol.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS, bandwidthLimitDetail.getValue(), false)); - } - DiskOfferingDetailVO iopsLimitDetail = _diskOfferingDetailDao.findDetail(offering.getId(), Volume.IOPS_LIMIT); - if (iopsLimitDetail != null) { - volumeDetailsVO.add(new VolumeDetailVO(vol.getId(), Volume.IOPS_LIMIT, iopsLimitDetail.getValue(), false)); - } - if (!volumeDetailsVO.isEmpty()) { - _volDetailDao.saveDetails(volumeDetailsVO); - } + saveVolumeDetails(offering.getId(), vol.getId()); if (StringUtils.isNotBlank(configurationId)) { VolumeDetailVO deployConfigurationDetail = new VolumeDetailVO(vol.getId(), VmDetailConstants.DEPLOY_AS_IS_CONFIGURATION, configurationId, false); @@ -982,6 +959,22 @@ private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering return toDiskProfile(vol, offering); } + @Override + public void saveVolumeDetails(Long diskOfferingId, Long volumeId) { + List volumeDetailsVO = new ArrayList<>(); + DiskOfferingDetailVO bandwidthLimitDetail = _diskOfferingDetailDao.findDetail(diskOfferingId, Volume.BANDWIDTH_LIMIT_IN_MBPS); + if (bandwidthLimitDetail != null) { + volumeDetailsVO.add(new VolumeDetailVO(volumeId, Volume.BANDWIDTH_LIMIT_IN_MBPS, bandwidthLimitDetail.getValue(), false)); + } + DiskOfferingDetailVO iopsLimitDetail = _diskOfferingDetailDao.findDetail(diskOfferingId, Volume.IOPS_LIMIT); + if (iopsLimitDetail != null) { + volumeDetailsVO.add(new VolumeDetailVO(volumeId, Volume.IOPS_LIMIT, iopsLimitDetail.getValue(), false)); + } + if (!volumeDetailsVO.isEmpty()) { + _volDetailDao.saveDetails(volumeDetailsVO); + } + } + @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating ROOT volume", create = true) @Override public List allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 2e8b36da446a..6c7663f7d192 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -1711,11 +1711,7 @@ public boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitio return _volStateMachine.transitTo(vol, event, null, _volsDao); } - @Override - @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DESTROY, eventDescription = "destroying a volume") - public Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge) { - VolumeVO volume = retrieveAndValidateVolume(volumeId, caller); - + public void validateDestroyVolume(Volume volume, Account caller, boolean expunge, boolean forceExpunge) { if (expunge) { // When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVolume is false for the caller. final Long userId = caller.getAccountId(); @@ -1725,6 +1721,14 @@ public Volume destroyVolume(long volumeId, Account caller, boolean expunge, bool } else if (volume.getState() == Volume.State.Allocated || volume.getState() == Volume.State.Uploaded) { throw new InvalidParameterValueException("The volume in Allocated/Uploaded state can only be expunged not destroyed/recovered"); } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DESTROY, eventDescription = "destroying a volume") + public Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge) { + VolumeVO volume = retrieveAndValidateVolume(volumeId, caller); + + validateDestroyVolume(volume, caller, expunge, forceExpunge); destroyVolumeIfPossible(volume); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 443346827ad3..04a742cba192 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7661,11 +7661,14 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, Long rootDiskOfferingId = cmd.getRootDiskOfferingId(); Long rootDiskSize = cmd.getRootDiskSize(); boolean expunge = cmd.getExpungeRootDisk(); + Map details = cmd.getDetails(); if (rootDiskSize != null && rootDiskSize < 0) { throw new InvalidParameterValueException("Invalid disk size " + rootDiskSize); } + verifyDetails(details); + UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Cannot find VM with ID " + vmId); @@ -7682,16 +7685,16 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, 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); - return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); + return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge, details); } - public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException { - return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge); + public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge, Map details) throws InsufficientCapacityException, ResourceUnavailableException { + return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge, details); } public UserVm restoreVMInternal(Account caller, UserVmVO vm) throws InsufficientCapacityException, ResourceUnavailableException { - return restoreVMInternal(caller, vm, null, null, null, false); + return restoreVMInternal(caller, vm, null, null, null, false, null); } private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List rootVols, UserVmVO vm) { @@ -7737,8 +7740,8 @@ private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTe @Override public UserVm restoreVirtualMachine(final Account caller, final long vmId, final Long newTemplateId, - final Long rootDiskOfferingId, final Long rootDiskSize, - final boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException { + final Long rootDiskOfferingId, + final boolean expunge, final Map details) throws InsufficientCapacityException, ResourceUnavailableException { Long userId = caller.getId(); _userDao.findById(userId); UserVmVO vm = _vmDao.findById(vmId); @@ -7798,6 +7801,7 @@ public UserVm restoreVirtualMachine(final Account caller, final long vmId, final DiskOffering diskOffering = rootDiskOfferingId != null ? _diskOfferingDao.findById(rootDiskOfferingId) : null; for (VolumeVO root : rootVols) { if ( !Volume.State.Allocated.equals(root.getState()) || newTemplateId != null ) { + _volumeService.validateDestroyVolume(root, caller, expunge, false); final UserVmVO userVm = vm; Pair vmAndNewVol = Transaction.execute(new TransactionCallbackWithException, CloudRuntimeException>() { @Override @@ -7829,7 +7833,8 @@ public Pair doInTransaction(final TransactionStatus status) th newVol = volumeMgr.allocateDuplicateVolume(root, null); } - updateVolume(newVol, template, userVm, diskOffering, rootDiskSize); + updateVolume(newVol, template, userVm, diskOffering, details); + volumeMgr.saveVolumeDetails(newVol.getDiskOfferingId(), newVol.getId()); // 1. Save usage event and update resource count for user vm volumes try { @@ -7922,7 +7927,7 @@ public Pair doInTransaction(final TransactionStatus status) th } - private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Long rootDiskSize) { + private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Map details) { VolumeVO resizedVolume = (VolumeVO) vol; if (userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !vol.getSize().equals(template.getSize())) { @@ -7930,13 +7935,37 @@ private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, Di resizedVolume.setSize(template.getSize()); } } + Long rootDiskSize = null; + if (StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) { + rootDiskSize = Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)); + } + if (diskOffering != null) { resizedVolume.setDiskOfferingId(diskOffering.getId()); resizedVolume.setSize(diskOffering.getDiskSize()); + if (diskOffering.isCustomized() && rootDiskSize == null) { + resizedVolume.setSize(vol.getSize()); + } + if (diskOffering.getMinIops() != null) { + resizedVolume.setMinIops(diskOffering.getMinIops()); + } + if (diskOffering.getMaxIops() != null) { + resizedVolume.setMaxIops(diskOffering.getMaxIops()); + } } if (rootDiskSize != null) { resizedVolume.setSize(rootDiskSize); } + + String minIops = details.get("minIops"); + String maxIops = details.get("maxIops"); + + if (StringUtils.isNotBlank(minIops)) { + resizedVolume.setMinIops(Long.parseLong(minIops)); + } + if (StringUtils.isNotBlank(maxIops)) { + resizedVolume.setMinIops(Long.parseLong(maxIops)); + } _volsDao.update(resizedVolume.getId(), resizedVolume); } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 6091720631b2..303a9b08b1ca 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -1275,7 +1275,7 @@ public void testRestoreVirtualMachineNoOwner() throws ResourceUnavailableExcepti when(userVmVoMock.getAccountId()).thenReturn(accountId); when(accountDao.findById(accountId)).thenReturn(null); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } @Test(expected = PermissionDeniedException.class) @@ -1289,7 +1289,7 @@ public void testRestoreVirtualMachineOwnerDisabled() throws ResourceUnavailableE when(accountDao.findById(accountId)).thenReturn(callerAccount); when(callerAccount.getState()).thenReturn(Account.State.DISABLED); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } @Test(expected = CloudRuntimeException.class) @@ -1304,7 +1304,7 @@ public void testRestoreVirtualMachineNotInRightState() throws ResourceUnavailabl when(accountDao.findById(accountId)).thenReturn(callerAccount); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } @Test(expected = InvalidParameterValueException.class) @@ -1325,7 +1325,7 @@ public void testRestoreVirtualMachineNoRootVolume() throws ResourceUnavailableEx when(templateDao.findById(currentTemplateId)).thenReturn(currentTemplate); when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(new ArrayList()); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } @Test(expected = InvalidParameterValueException.class) @@ -1352,7 +1352,7 @@ public void testRestoreVirtualMachineMoreThanOneRootVolume() throws ResourceUnav volumes.add(rootVolume2); when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(volumes); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } @Test(expected = InvalidParameterValueException.class) @@ -1379,6 +1379,6 @@ public void testRestoreVirtualMachineWithVMSnapshots() throws ResourceUnavailabl vmSnapshots.add(vmSnapshot); when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots); - userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, null, false); + userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 0c52107c87f9..534118673714 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -164,33 +164,10 @@ export default { label: 'label.reinstall.vm', message: 'message.reinstall.vm', dataView: true, - args: ['virtualmachineid', 'templateid', 'diskofferingid', 'rootdisksize', 'expunge'], - filters: (record) => { - var filters = {} - var filterParams = {} - filterParams.hypervisortype = record.hypervisor - filterParams.zoneid = record.zoneid - filters.templateid = filterParams - return filters - }, + popup: true, show: (record) => { return ['Running', 'Stopped'].includes(record.state) }, - mapping: { - virtualmachineid: { - value: (record) => { return record.id } - } - }, disabled: (record) => { return record.hostcontrolstate === 'Offline' }, - successMethod: (obj, result) => { - const vm = result.jobresult.virtualmachine || {} - if (result.jobstatus === 1 && vm.password) { - const name = vm.displayname || vm.name || vm.id - obj.$notification.success({ - message: `${obj.$t('label.reinstall.vm')}: ` + name, - description: `${obj.$t('label.password.reset.confirm')}: ` + vm.password, - duration: 0 - }) - } - } + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ReinstallVm.vue'))) }, { api: 'createVMSnapshot', diff --git a/ui/src/views/compute/ReinstallVm.vue b/ui/src/views/compute/ReinstallVm.vue new file mode 100644 index 000000000000..14e464de5ba8 --- /dev/null +++ b/ui/src/views/compute/ReinstallVm.vue @@ -0,0 +1,297 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + + + From 6229582e952efef3ff1e48ca12db2044cce51a27 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 4 Apr 2024 13:21:07 +0530 Subject: [PATCH 08/14] minor cleanup --- .../api/command/user/vm/RestoreVMCmd.java | 4 ---- .../java/com/cloud/vm/VirtualMachineManager.java | 2 +- .../com/cloud/vm/VirtualMachineManagerImpl.java | 2 +- .../src/main/java/com/cloud/vm/VmWorkRestore.java | 1 - .../main/java/com/cloud/vm/UserVmManagerImpl.java | 13 ++++--------- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git 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 index c2177c7451ae..17c4e97eb3b7 100644 --- 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 @@ -142,10 +142,6 @@ public Long getRootDiskOfferingId() { return rootDiskOfferingId; } - public Long getRootDiskSize() { - return rootDiskSize != null ? rootDiskSize * 1024L * 1024L * 1024L : null; - } - public Map getDetails() { Map customparameterMap = convertDetailsToMap(details); if (rootDiskSize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 81125b22faf5..3f7d6be6d889 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -254,7 +254,7 @@ static String getHypervisorHostname(String name) { */ boolean unmanage(String vmUuid); - UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge, Map details) throws ResourceUnavailableException, InsufficientCapacityException; + UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map details) throws ResourceUnavailableException, InsufficientCapacityException; boolean checkIfVmHasClusterWideVolumes(Long vmId); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index a4173be35d3f..243613907ff4 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5623,7 +5623,7 @@ protected void resourceCountDecrement (long accountId, Long cpu, Long memory) { } @Override - public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge, final Map details) throws ResourceUnavailableException, InsufficientCapacityException { + public UserVm restoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final Map details) throws ResourceUnavailableException, InsufficientCapacityException { final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { VmWorkJobVO placeHolder = null; diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java index 6a79bfe60f4a..ab5425a25000 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java @@ -23,7 +23,6 @@ public class VmWorkRestore extends VmWork { private Long templateId; private Long rootDiskOfferingId; - private Long rootDiskSize; private Map details; private boolean expunge; diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 04a742cba192..b57f25d3d44f 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7659,14 +7659,9 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, long vmId = cmd.getVmId(); Long newTemplateId = cmd.getTemplateId(); Long rootDiskOfferingId = cmd.getRootDiskOfferingId(); - Long rootDiskSize = cmd.getRootDiskSize(); boolean expunge = cmd.getExpungeRootDisk(); Map details = cmd.getDetails(); - if (rootDiskSize != null && rootDiskSize < 0) { - throw new InvalidParameterValueException("Invalid disk size " + rootDiskSize); - } - verifyDetails(details); UserVmVO vm = _vmDao.findById(vmId); @@ -7685,16 +7680,16 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, 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); - return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge, details); + return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, expunge, details); } - public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge, Map details) throws InsufficientCapacityException, ResourceUnavailableException { - return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge, details); + public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map 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, null, false, null); + return restoreVMInternal(caller, vm, null, null, false, null); } private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List rootVols, UserVmVO vm) { From 81b3fdf6823860e456bd266a2a81cafff39ff3ff Mon Sep 17 00:00:00 2001 From: Vishesh Date: Fri, 5 Apr 2024 11:04:34 +0530 Subject: [PATCH 09/14] fixup --- .../java/com/cloud/vm/UserVmManagerImpl.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index b57f25d3d44f..bd4ead0e959f 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7930,15 +7930,11 @@ private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, Di resizedVolume.setSize(template.getSize()); } } - Long rootDiskSize = null; - if (StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) { - rootDiskSize = Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)); - } if (diskOffering != null) { resizedVolume.setDiskOfferingId(diskOffering.getId()); resizedVolume.setSize(diskOffering.getDiskSize()); - if (diskOffering.isCustomized() && rootDiskSize == null) { + if (diskOffering.isCustomized()) { resizedVolume.setSize(vol.getSize()); } if (diskOffering.getMinIops() != null) { @@ -7948,18 +7944,25 @@ private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, Di resizedVolume.setMaxIops(diskOffering.getMaxIops()); } } - if (rootDiskSize != null) { - resizedVolume.setSize(rootDiskSize); - } - String minIops = details.get("minIops"); - String maxIops = details.get("maxIops"); + if (MapUtils.isNotEmpty(details)) { + Long rootDiskSize = null; + if (StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) { + rootDiskSize = Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)); + } + if (rootDiskSize != null) { + resizedVolume.setSize(rootDiskSize); + } - if (StringUtils.isNotBlank(minIops)) { - resizedVolume.setMinIops(Long.parseLong(minIops)); - } - if (StringUtils.isNotBlank(maxIops)) { - resizedVolume.setMinIops(Long.parseLong(maxIops)); + String minIops = details.get("minIops"); + String maxIops = details.get("maxIops"); + + if (StringUtils.isNumeric(minIops)) { + resizedVolume.setMinIops(Long.parseLong(minIops)); + } + if (StringUtils.isNumeric(maxIops)) { + resizedVolume.setMinIops(Long.parseLong(maxIops)); + } } _volsDao.update(resizedVolume.getId(), resizedVolume); } From 20f54a52b45929cad49ca04c3dcdeecc72fcc70b Mon Sep 17 00:00:00 2001 From: Vishesh Date: Fri, 5 Apr 2024 17:59:05 +0530 Subject: [PATCH 10/14] Add checks for template size --- .../java/com/cloud/vm/UserVmManagerImpl.java | 17 +++++++++++++---- ui/src/views/compute/ReinstallVm.vue | 8 ++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index bd4ead0e959f..a24c94bcc5cc 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7673,6 +7673,18 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, _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 < template.getSize()) { + throw new InvalidParameterValueException(String.format("Root disk size [%s] is smaller than the template size [%s]", rootDiskSize, template.getSize())); + } + } else if (diskOffering != null && templateSize < diskOffering.getDiskSize()) { + 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); @@ -7946,11 +7958,8 @@ private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, Di } if (MapUtils.isNotEmpty(details)) { - Long rootDiskSize = null; if (StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) { - rootDiskSize = Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)); - } - if (rootDiskSize != null) { + Long rootDiskSize = Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)) * GiB_TO_BYTES; resizedVolume.setSize(rootDiskSize); } diff --git a/ui/src/views/compute/ReinstallVm.vue b/ui/src/views/compute/ReinstallVm.vue index 14e464de5ba8..92414b7cd5f7 100644 --- a/ui/src/views/compute/ReinstallVm.vue +++ b/ui/src/views/compute/ReinstallVm.vue @@ -89,7 +89,7 @@ v-if="overrideDiskSize" input-decorator="rootdisksize" :isCustomized="true" - @update-disk-size="(input, value) => updateFieldValue(overrideRootDiskSize, value)" + @update-disk-size="(input, value) => updateFieldValue('overrideRootDiskSize', value)" style="margin-top: 10px;" /> @@ -153,7 +153,7 @@ export default { rootDiskSizeKey: 'details[0].rootdisksize', minIopsKey: 'details[0].minIops', maxIopsKey: 'details[0].maxIops', - rootDiskSize: 0, + rootdisksize: 0, minIops: 0, maxIops: 0, templateFilter: [ @@ -192,14 +192,14 @@ export default { if (this.overrideDiskOffering) { params.diskofferingid = this.diskOffering.id if (this.diskOffering.iscustomized) { - params[this.rootDiskSizeKey] = this.rootDiskSize + params[this.rootDiskSizeKey] = this.rootdisksize } if (this.diskOffering.iscustomizediops) { params[this.minIopsKey] = this.minIops params[this.maxIopsKey] = this.maxIops } } - if (this.overrideDiskSize && this.overrideRootDiskSize && !this.rootdisksize) { + if (this.overrideDiskSize && this.overrideRootDiskSize) { params.rootdisksize = this.overrideRootDiskSize } params.expunge = this.expungeDisk From 856b22ef4d0b962d8dd253fe5feec08eab1ff2e7 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Mon, 8 Apr 2024 14:05:27 +0530 Subject: [PATCH 11/14] Replace strings for IOPS with constants --- .../orchestration/CloudOrchestrator.java | 7 +++++-- .../java/com/cloud/vm/UserVmManagerImpl.java | 18 ++++++++++-------- .../cloudstack/vm/UnmanagedVMsManagerImpl.java | 11 +++++++---- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java index d639b4513e4a..6763a13aed63 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java @@ -61,6 +61,9 @@ import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS; +import static org.apache.cloudstack.api.ApiConstants.MIN_IOPS; + @Component public class CloudOrchestrator implements OrchestrationService { @@ -196,8 +199,8 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); if (userVmDetails != null) { - String minIops = userVmDetails.get("minIops"); - String maxIops = userVmDetails.get("maxIops"); + String minIops = userVmDetails.get(MIN_IOPS); + String maxIops = userVmDetails.get(MAX_IOPS); rootDiskOfferingInfo.setMinIops(minIops != null && minIops.trim().length() > 0 ? Long.parseLong(minIops) : null); rootDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index a24c94bcc5cc..dfd9829157b8 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -18,6 +18,8 @@ import static com.cloud.configuration.ConfigurationManagerImpl.VM_USERDATA_MAX_LENGTH; import static com.cloud.utils.NumbersUtil.toHumanReadableSize; +import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS; +import static org.apache.cloudstack.api.ApiConstants.MIN_IOPS; import java.io.IOException; import java.io.StringReader; @@ -2150,11 +2152,11 @@ private void changeDiskOfferingForRootVolume(Long vmId, DiskOfferingVO newDiskOf Long maxIopsInNewDiskOffering = null; boolean autoMigrate = false; boolean shrinkOk = false; - if (customParameters.containsKey(ApiConstants.MIN_IOPS)) { - minIopsInNewDiskOffering = Long.parseLong(customParameters.get(ApiConstants.MIN_IOPS)); + if (customParameters.containsKey(MIN_IOPS)) { + minIopsInNewDiskOffering = Long.parseLong(customParameters.get(MIN_IOPS)); } - if (customParameters.containsKey(ApiConstants.MAX_IOPS)) { - minIopsInNewDiskOffering = Long.parseLong(customParameters.get(ApiConstants.MAX_IOPS)); + if (customParameters.containsKey(MAX_IOPS)) { + minIopsInNewDiskOffering = Long.parseLong(customParameters.get(MAX_IOPS)); } if (customParameters.containsKey(ApiConstants.AUTO_MIGRATE)) { autoMigrate = Boolean.parseBoolean(customParameters.get(ApiConstants.AUTO_MIGRATE)); @@ -6310,8 +6312,8 @@ protected List getSecurityGroupIdList(SecurityGroupAction cmd, DataCenter // if specified, minIops should be <= maxIops private void verifyDetails(Map details) { if (details != null) { - String minIops = details.get("minIops"); - String maxIops = details.get("maxIops"); + String minIops = details.get(MIN_IOPS); + String maxIops = details.get(MAX_IOPS); verifyMinAndMaxIops(minIops, maxIops); @@ -7963,8 +7965,8 @@ private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO userVm, Di resizedVolume.setSize(rootDiskSize); } - String minIops = details.get("minIops"); - String maxIops = details.get("maxIops"); + String minIops = details.get(MIN_IOPS); + String maxIops = details.get(MAX_IOPS); if (StringUtils.isNumeric(minIops)) { resizedVolume.setMinIops(Long.parseLong(minIops)); 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 1ed5a8f1648a..5fe07694163d 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -184,6 +184,9 @@ import java.util.Set; import java.util.stream.Collectors; +import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS; +import static org.apache.cloudstack.api.ApiConstants.MIN_IOPS; + public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { public static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso"; public static final String KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME = "kvm-default-vm-import-dummy-template"; @@ -1165,12 +1168,12 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); } Long minIops = null; - if (details.containsKey("minIops")) { - minIops = Long.parseLong(details.get("minIops")); + if (details.containsKey(MIN_IOPS)) { + minIops = Long.parseLong(details.get(MIN_IOPS)); } Long maxIops = null; - if (details.containsKey("maxIops")) { - maxIops = Long.parseLong(details.get("maxIops")); + if (details.containsKey(MAX_IOPS)) { + maxIops = Long.parseLong(details.get(MAX_IOPS)); } DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); diskProfileStoragePoolList.add(importDisk(rootDisk, userVm, cluster, diskOffering, Volume.Type.ROOT, String.format("ROOT-%d", userVm.getId()), From 37de99c142944cb3010c495126bedb9dbe4da76c Mon Sep 17 00:00:00 2001 From: Vishesh Date: Mon, 8 Apr 2024 15:56:29 +0530 Subject: [PATCH 12/14] Fix saveVolumeDetails method --- .../engine/orchestration/VolumeOrchestrator.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 3ee227131cb3..77c95c83df6e 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -965,10 +965,20 @@ public void saveVolumeDetails(Long diskOfferingId, Long volumeId) { DiskOfferingDetailVO bandwidthLimitDetail = _diskOfferingDetailDao.findDetail(diskOfferingId, Volume.BANDWIDTH_LIMIT_IN_MBPS); if (bandwidthLimitDetail != null) { volumeDetailsVO.add(new VolumeDetailVO(volumeId, Volume.BANDWIDTH_LIMIT_IN_MBPS, bandwidthLimitDetail.getValue(), false)); + } else { + VolumeDetailVO bandwidthLimit = _volDetailDao.findDetail(volumeId, Volume.BANDWIDTH_LIMIT_IN_MBPS); + if (bandwidthLimit != null) { + _volDetailDao.remove(bandwidthLimit.getId()); + } } DiskOfferingDetailVO iopsLimitDetail = _diskOfferingDetailDao.findDetail(diskOfferingId, Volume.IOPS_LIMIT); if (iopsLimitDetail != null) { volumeDetailsVO.add(new VolumeDetailVO(volumeId, Volume.IOPS_LIMIT, iopsLimitDetail.getValue(), false)); + } else { + VolumeDetailVO iopsLimit = _volDetailDao.findDetail(volumeId, Volume.IOPS_LIMIT); + if (iopsLimit != null) { + _volDetailDao.remove(iopsLimit.getId()); + } } if (!volumeDetailsVO.isEmpty()) { _volDetailDao.saveDetails(volumeDetailsVO); From c5cf3589c7be87f1f643a030a0805b4ff432536f Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 10 Apr 2024 17:37:09 +0530 Subject: [PATCH 13/14] Fixup --- server/src/main/java/com/cloud/vm/UserVmManagerImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index dfd9829157b8..87ccb1647512 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7680,10 +7680,10 @@ public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, String rootDiskSize = details.get(VmDetailConstants.ROOT_DISK_SIZE); Long templateSize = template.getSize(); if (StringUtils.isNumeric(rootDiskSize)) { - if (Long.parseLong(rootDiskSize) * GiB_TO_BYTES < template.getSize()) { - throw new InvalidParameterValueException(String.format("Root disk size [%s] is smaller than the template size [%s]", rootDiskSize, template.getSize())); + 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 && templateSize < diskOffering.getDiskSize()) { + } 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)); } } From 4e59e308f413ac2e07e3b5fb51b04c7099f49951 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 11 Apr 2024 00:43:02 +0530 Subject: [PATCH 14/14] Fixup UI styling --- ui/src/views/compute/ReinstallVm.vue | 42 +++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/ui/src/views/compute/ReinstallVm.vue b/ui/src/views/compute/ReinstallVm.vue index 92414b7cd5f7..ee07011fe283 100644 --- a/ui/src/views/compute/ReinstallVm.vue +++ b/ui/src/views/compute/ReinstallVm.vue @@ -26,9 +26,9 @@ show-icon > + style="margin-bottom: 5px" + v-html="$t('message.reinstall.vm')" + /> - + - + - + -