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

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


The following commit(s) were added to refs/heads/4.19 by this push:
     new b998e7dbb63 Allow overriding root disk offering & size, and expunge 
old root disk while restoring a VM (#8800)
b998e7dbb63 is described below

commit b998e7dbb6355c4165b00a2792062f2d446713dd
Author: Vishesh <vishes...@gmail.com>
AuthorDate: Fri Apr 12 17:47:52 2024 +0530

    Allow overriding root disk offering & size, and expunge old root disk while 
restoring a VM (#8800)
    
    * Allow overriding root diskoffering id & size while restoring VM
    
    * UI changes
    
    * Allow expunging of old disk while restoring a VM
    
    * Resolve comments
    
    * Address comments
    
    * Duplicate volume's details while duplicating volume
    
    * Allow setting IOPS for the new volume
    
    * minor cleanup
    
    * fixup
    
    * Add checks for template size
    
    * Replace strings for IOPS with constants
    
    * Fix saveVolumeDetails method
    
    * Fixup
    
    * Fixup UI styling
---
 .../java/com/cloud/storage/VolumeApiService.java   |   2 +
 api/src/main/java/com/cloud/vm/UserVmService.java  |   2 +-
 .../api/command/user/vm/RestoreVMCmd.java          |  42 +++
 .../java/com/cloud/vm/VirtualMachineManager.java   |   2 +-
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  18 +-
 .../src/main/java/com/cloud/vm/VmWorkRestore.java  |  27 +-
 .../engine/orchestration/CloudOrchestrator.java    |   7 +-
 .../engine/orchestration/VolumeOrchestrator.java   |  39 ++-
 .../com/cloud/storage/VolumeApiServiceImpl.java    |  14 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 124 +++++++--
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     |  11 +-
 .../java/com/cloud/vm/UserVmManagerImplTest.java   |  26 +-
 ui/src/config/section/compute.js                   |  27 +-
 ui/src/views/compute/ReinstallVm.vue               | 307 +++++++++++++++++++++
 14 files changed, 541 insertions(+), 107 deletions(-)

diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java 
b/api/src/main/java/com/cloud/storage/VolumeApiService.java
index a673df12d0f..4f09702b7db 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 @@ public interface VolumeApiService {
 
     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 c32c099ed3a..787ed7bde37 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 @@ public interface UserVmService {
 
     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 rootDiskOfferingId, boolean expunge, Map<String, String> 
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 4b59bf560cb..17c4e97eb3b 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,7 +16,9 @@
 // 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;
 
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@@ -42,6 +44,8 @@ import com.cloud.user.Account;
 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)
@@ -60,6 +64,28 @@ 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 rootDiskOfferingId;
+
+    @Parameter(name = ApiConstants.ROOT_DISK_SIZE,
+               type = CommandType.LONG,
+               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.",
+               since = "4.19.1")
+    private Boolean expungeRootDisk;
+
     @Override
     public String getEventType() {
         return EventTypes.EVENT_VM_RESTORE;
@@ -112,6 +138,22 @@ public class RestoreVMCmd extends BaseAsyncCmd implements 
UserCmd {
         return getVmId();
     }
 
+    public Long getRootDiskOfferingId() {
+        return rootDiskOfferingId;
+    }
+
+    public Map<String, String> getDetails() {
+        Map<String, String> 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;
+    }
+
     @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 8cd67f25331..3f7d6be6d88 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 @@ public interface VirtualMachineManager extends Manager {
      */
     boolean unmanage(String vmUuid);
 
-    UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws 
ResourceUnavailableException, InsufficientCapacityException;
+    UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long 
rootDiskOfferingId, boolean expunge, Map<String, String> 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 59d129bc065..243613907ff 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 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
     }
 
     @Override
-    public UserVm restoreVirtualMachine(final long vmId, final Long 
newTemplateId) throws ResourceUnavailableException, 
InsufficientCapacityException {
+    public UserVm restoreVirtualMachine(final long vmId, final Long 
newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final 
Map<String, String> details) throws ResourceUnavailableException, 
InsufficientCapacityException {
         final AsyncJobExecutionContext jobContext = 
AsyncJobExecutionContext.getCurrentExecutionContext();
         if 
(jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
             VmWorkJobVO placeHolder = null;
             placeHolder = createPlaceHolderWork(vmId);
             try {
-                return orchestrateRestoreVirtualMachine(vmId, newTemplateId);
+                return orchestrateRestoreVirtualMachine(vmId, newTemplateId, 
rootDiskOfferingId, expunge, details);
             } finally {
                 if (placeHolder != null) {
                     _workJobDao.expunge(placeHolder.getId());
                 }
             }
         } else {
-            final Outcome<VirtualMachine> outcome = 
restoreVirtualMachineThroughJobQueue(vmId, newTemplateId);
+            final Outcome<VirtualMachine> outcome = 
restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, rootDiskOfferingId, 
expunge, details);
 
             retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), 
"restoreVirtualMachine");
 
@@ -5653,14 +5653,14 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
         }
     }
 
-    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 rootDiskOfferingId, final boolean expunge, final 
Map<String, String> details) throws ResourceUnavailableException, 
InsufficientCapacityException {
+        s_logger.debug("Restoring vm " + vmId + " with templateId : " + 
newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " details : " + 
details);
         final CallContext context = CallContext.current();
         final Account account = context.getCallingAccount();
-        return _userVmService.restoreVirtualMachine(account, vmId, 
newTemplateId);
+        return _userVmService.restoreVirtualMachine(account, vmId, 
newTemplateId, rootDiskOfferingId, expunge, details);
     }
 
-    public Outcome<VirtualMachine> restoreVirtualMachineThroughJobQueue(final 
long vmId, final Long newTemplateId) {
+    public Outcome<VirtualMachine> restoreVirtualMachineThroughJobQueue(final 
long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final 
boolean expunge, Map<String, String> details) {
         String commandName = VmWorkRestore.class.getName();
         Pair<VmWorkJobVO, Long> pendingWorkJob = retrievePendingWorkJob(vmId, 
commandName);
 
@@ -5670,7 +5670,7 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
             Pair<VmWorkJobVO, VmWork> newVmWorkJobAndInfo = 
createWorkJobAndWorkInfo(commandName, vmId);
 
             workJob = newVmWorkJobAndInfo.first();
-            VmWorkRestore workInfo = new 
VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId);
+            VmWorkRestore workInfo = new 
VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, rootDiskOfferingId, 
expunge, details);
 
             setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId);
         }
@@ -5682,7 +5682,7 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
     @ReflectionUse
     private Pair<JobInfo.Status, String> 
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.getRootDiskOfferingId(), work.getExpunge(), 
work.getDetails());
         HashMap<Long, String> 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 cb3adae27aa..ab5425a2500 100644
--- a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java
+++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java
@@ -16,23 +16,38 @@
 // 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 Map<String,String> details;
 
-    public VmWorkRestore(long userId, long accountId, long vmId, String 
handlerName, Long templateId) {
-        super(userId, accountId, vmId, handlerName);
+    private boolean expunge;
 
-        this.templateId = templateId;
-    }
-
-    public VmWorkRestore(VmWork vmWork, Long templateId) {
+    public VmWorkRestore(VmWork vmWork, Long templateId, Long 
rootDiskOfferingId, boolean expunge, Map<String,String> details) {
         super(vmWork);
         this.templateId = templateId;
+        this.rootDiskOfferingId = rootDiskOfferingId;
+        this.expunge = expunge;
+        this.details = details;
     }
 
     public Long getTemplateId() {
         return templateId;
     }
+
+    public Long getRootDiskOfferingId() {
+        return rootDiskOfferingId;
+    }
+
+    public boolean getExpunge() {
+        return expunge;
+    }
+
+    public Map<String, String> getDetails() {
+        return details;
+    }
 }
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 d639b4513e4..6763a13aed6 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.UserVmDao;
 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 class CloudOrchestrator implements 
OrchestrationService {
             Map<String, String> 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/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 3a5b342b6e8..5c79fb64d8d 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
@@ -949,18 +949,7 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
 
         vol = _volsDao.persist(vol);
 
-        List<VolumeDetailVO> volumeDetailsVO = new ArrayList<VolumeDetailVO>();
-        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);
@@ -985,6 +974,32 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
         return toDiskProfile(vol, offering);
     }
 
+    @Override
+    public void saveVolumeDetails(Long diskOfferingId, Long volumeId) {
+        List<VolumeDetailVO> 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));
+        } 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);
+        }
+    }
+
     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription 
= "creating ROOT volume", create = true)
     @Override
     public List<DiskProfile> 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 e5a33a22859..8679221107d 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1724,11 +1724,7 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         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();
@@ -1738,6 +1734,14 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         } 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 ae0d66ee482..566fcb38fc9 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 @@ package com.cloud.vm;
 
 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;
@@ -566,6 +568,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;
@@ -2148,11 +2152,11 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 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));
@@ -3248,7 +3252,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         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");
@@ -6327,8 +6331,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     // if specified, minIops should be <= maxIops
     private void verifyDetails(Map<String,String> 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);
 
@@ -7660,6 +7664,20 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         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
@@ -7667,6 +7685,11 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
 
         long vmId = cmd.getVmId();
         Long newTemplateId = cmd.getTemplateId();
+        Long rootDiskOfferingId = cmd.getRootDiskOfferingId();
+        boolean expunge = cmd.getExpungeRootDisk();
+        Map<String, String> details = cmd.getDetails();
+
+        verifyDetails(details);
 
         UserVmVO vm = _vmDao.findById(vmId);
         if (vm == null) {
@@ -7674,20 +7697,38 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             ex.addProxyObject(String.valueOf(vmId), "vmId");
             throw ex;
         }
-
         _accountMgr.checkAccess(caller, null, true, vm);
 
+        DiskOffering diskOffering = rootDiskOfferingId != null ? 
validateAndGetDiskOffering(rootDiskOfferingId, vm, caller) : null;
+        VMTemplateVO template = _templateDao.findById(newTemplateId);
+        if (template.getSize() != null) {
+            String rootDiskSize = 
details.get(VmDetailConstants.ROOT_DISK_SIZE);
+            Long templateSize = template.getSize();
+            if (StringUtils.isNumeric(rootDiskSize)) {
+                if (Long.parseLong(rootDiskSize) * GiB_TO_BYTES < 
templateSize) {
+                    throw new 
InvalidParameterValueException(String.format("Root disk size [%s] is smaller 
than the template size [%s]", rootDiskSize, templateSize));
+                }
+            } else if (diskOffering != null && diskOffering.getDiskSize() < 
templateSize) {
+                throw new InvalidParameterValueException(String.format("Disk 
size for selected offering [%s] is less than the template's size [%s]", 
diskOffering.getDiskSize(), templateSize));
+            }
+        }
+
         //check if there are any active snapshots on volumes associated with 
the VM
         s_logger.debug("Checking if there are any ongoing snapshots on the 
ROOT volumes associated with VM with ID " + vmId);
         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, 
rootDiskOfferingId, expunge, details);
     }
 
-    public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long 
newTemplateId) throws InsufficientCapacityException, 
ResourceUnavailableException {
-        return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId);
+    public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long 
newTemplateId, Long rootDiskOfferingId, boolean expunge, Map<String, String> 
details) throws InsufficientCapacityException, ResourceUnavailableException {
+        return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, 
rootDiskOfferingId, expunge, details);
+    }
+
+
+    public UserVm restoreVMInternal(Account caller, UserVmVO vm) throws 
InsufficientCapacityException, ResourceUnavailableException {
+        return restoreVMInternal(caller, vm, null, null, false, null);
     }
 
     private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long 
newTemplateId, List<VolumeVO> rootVols, UserVmVO vm) {
@@ -7732,7 +7773,9 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     }
 
     @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 rootDiskOfferingId,
+            final boolean expunge, final Map<String, String> details) throws 
InsufficientCapacityException, ResourceUnavailableException {
         Long userId = caller.getId();
         _userDao.findById(userId);
         UserVmVO vm = _vmDao.findById(vmId);
@@ -7789,9 +7832,10 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             }
         }
 
-        List<Volume> newVols = new ArrayList<>();
+        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<UserVmVO, Volume> vmAndNewVol = Transaction.execute(new 
TransactionCallbackWithException<Pair<UserVmVO, Volume>, 
CloudRuntimeException>() {
                     @Override
@@ -7822,15 +7866,9 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                         } else {
                             newVol = volumeMgr.allocateDuplicateVolume(root, 
null);
                         }
-                        newVols.add(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);
-                            }
-                        }
+                        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 {
@@ -7860,7 +7898,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
 
                 // Detach, destroy and create the usage event for the old root 
volume.
                 _volsDao.detachVolume(root.getId());
-                volumeMgr.destroyVolume(root);
+                _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) {
@@ -7923,6 +7961,48 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
 
     }
 
+    private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO 
userVm, DiskOffering diskOffering, Map<String, String> details) {
+        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 (diskOffering.isCustomized()) {
+                resizedVolume.setSize(vol.getSize());
+            }
+            if (diskOffering.getMinIops() != null) {
+                resizedVolume.setMinIops(diskOffering.getMinIops());
+            }
+            if (diskOffering.getMaxIops() != null) {
+                resizedVolume.setMaxIops(diskOffering.getMaxIops());
+            }
+        }
+
+        if (MapUtils.isNotEmpty(details)) {
+            if 
(StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) {
+                Long rootDiskSize = 
Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)) * GiB_TO_BYTES;
+                resizedVolume.setSize(rootDiskSize);
+            }
+
+            String minIops = details.get(MIN_IOPS);
+            String maxIops = details.get(MAX_IOPS);
+
+            if (StringUtils.isNumeric(minIops)) {
+                resizedVolume.setMinIops(Long.parseLong(minIops));
+            }
+            if (StringUtils.isNumeric(maxIops)) {
+                resizedVolume.setMinIops(Long.parseLong(maxIops));
+            }
+        }
+        _volsDao.update(resizedVolume.getId(), resizedVolume);
+    }
+
     private void updateVMDynamicallyScalabilityUsingTemplate(UserVmVO vm, Long 
newTemplateId) {
         ServiceOfferingVO serviceOffering = 
serviceOfferingDao.findById(vm.getServiceOfferingId());
         VMTemplateVO newTemplate = _templateDao.findById(newTemplateId);
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 069f749e359..e809ebb8a88 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.Random;
 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";
@@ -1166,12 +1169,12 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
                 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()),
diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java 
b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
index e2c2b8ef9e2..303a9b08b1c 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.service.dao.ServiceOfferingDao;
 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 class UserVmManagerImplTest {
         when(cmd.getTemplateId()).thenReturn(2L);
         when(userVmDao.findById(vmId)).thenReturn(userVmVoMock);
 
-        List<VolumeVO> 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<SnapshotVO> 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 class UserVmManagerImplTest {
         when(userVmVoMock.getAccountId()).thenReturn(accountId);
         when(accountDao.findById(accountId)).thenReturn(null);
 
-        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId);
+        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId, null, false, null);
     }
 
     @Test(expected = PermissionDeniedException.class)
@@ -1303,7 +1289,7 @@ public class UserVmManagerImplTest {
         when(accountDao.findById(accountId)).thenReturn(callerAccount);
         when(callerAccount.getState()).thenReturn(Account.State.DISABLED);
 
-        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId);
+        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId, null, false, null);
     }
 
     @Test(expected = CloudRuntimeException.class)
@@ -1318,7 +1304,7 @@ public class UserVmManagerImplTest {
         when(accountDao.findById(accountId)).thenReturn(callerAccount);
         
when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting);
 
-        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId);
+        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId, null, false, null);
     }
 
     @Test(expected = InvalidParameterValueException.class)
@@ -1339,7 +1325,7 @@ public class UserVmManagerImplTest {
         
when(templateDao.findById(currentTemplateId)).thenReturn(currentTemplate);
         when(volumeDaoMock.findByInstanceAndType(vmId, 
Volume.Type.ROOT)).thenReturn(new ArrayList<VolumeVO>());
 
-        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId);
+        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId, null, false, null);
     }
 
     @Test(expected = InvalidParameterValueException.class)
@@ -1366,7 +1352,7 @@ public class UserVmManagerImplTest {
         volumes.add(rootVolume2);
         when(volumeDaoMock.findByInstanceAndType(vmId, 
Volume.Type.ROOT)).thenReturn(volumes);
 
-        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId);
+        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId, null, false, null);
     }
 
     @Test(expected = InvalidParameterValueException.class)
@@ -1393,6 +1379,6 @@ public class UserVmManagerImplTest {
         vmSnapshots.add(vmSnapshot);
         when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots);
 
-        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId);
+        userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, 
newTemplateId, null, false, null);
     }
 }
diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js
index 9390d2a7d62..db17b20ef9d 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'],
-          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 00000000000..ee07011fe28
--- /dev/null
+++ b/ui/src/views/compute/ReinstallVm.vue
@@ -0,0 +1,307 @@
+// 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.
+
+<template>
+  <a-form
+    v-ctrl-enter="handleSubmit"
+    @finish="handleSubmit"
+    layout="vertical"
+  >
+    <a-alert
+      type="warning"
+      show-icon
+    >
+      <template #message><span
+          style="margin-bottom: 5px"
+          v-html="$t('message.reinstall.vm')"
+        /></template>
+    </a-alert>
+    <a-form-item>
+      <template-iso-selection
+        input-decorator="templateid"
+        :items="templates"
+        :selected="tabKey"
+        :loading="loading.templates"
+        :preFillContent="resource.templateid"
+        :key="templateKey"
+        @handle-search-filter="($event) => fetchAllTemplates($event)"
+        @update-template-iso="updateFieldValue"
+      />
+    </a-form-item>
+    <a-form-item>
+      <template #label>
+        <tooltip-label
+          :title="$t('label.override.root.diskoffering')"
+          :tooltip="apiParams.diskofferingid.description"
+        />
+      </template>
+      <a-switch
+        v-model:checked="overrideDiskOffering"
+        @change="val => { overrideDiskOffering = val }"
+      />
+    </a-form-item>
+    <a-form-item v-if="overrideDiskOffering">
+      <disk-offering-selection
+        :items="diskOfferings"
+        :row-count="diskOfferingCount"
+        :zoneId="resource.zoneId"
+        :value="diskOffering ? diskOffering.id : ''"
+        :loading="loading.diskOfferings"
+        :preFillContent="resource.diskofferingid"
+        :isIsoSelected="false"
+        :isRootDiskOffering="true"
+        @on-selected-disk-size="onSelectDiskSize"
+        @handle-search-filter="($event) => fetchDiskOfferings($event)"
+      />
+    </a-form-item>
+    <a-form-item v-if="diskOffering && (diskOffering.iscustomized || 
diskOffering.iscustomizediops)">
+      <disk-size-selection
+        input-decorator="rootdisksize"
+        :diskSelected="diskOffering"
+        :isCustomized="diskOffering.iscustomized"
+        @handler-error="handlerError"
+        @update-disk-size="updateFieldValue"
+        @update-root-disk-iops-value="updateFieldValue"
+      />
+    </a-form-item>
+    <a-form-item v-if="!(diskOffering && diskOffering.iscustomized)">
+      <template #label>
+        <tooltip-label
+          :title="$t('label.override.rootdisk.size')"
+          :tooltip="apiParams.rootdisksize.description"
+        />
+      </template>
+      <a-switch
+        v-model:checked="overrideDiskSize"
+        @change="val => { overrideDiskSize = val }"
+      />
+      <disk-size-selection
+        v-if="overrideDiskSize"
+        input-decorator="rootdisksize"
+        :isCustomized="true"
+        @update-disk-size="(input, value) => 
updateFieldValue('overrideRootDiskSize', value)"
+        style="margin-top: 10px;"
+      />
+    </a-form-item>
+    <a-form-item>
+      <template #label>
+        <tooltip-label
+          :title="$t('label.expunge')"
+          :tooltip="apiParams.expunge.description"
+        />
+      </template>
+      <a-switch
+        v-model:checked="expungeDisk"
+        @change="val => { expungeDisk = val }"
+      />
+    </a-form-item>
+    <div
+      :span="24"
+      class="action-button"
+    >
+      <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
+      <a-button
+        ref="submit"
+        type="primary"
+        @click="handleSubmit"
+      >{{ $t('label.ok') }}</a-button>
+    </div>
+  </a-form>
+</template>
+
+<script>
+import { api } from '@/api'
+import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
+import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
+import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
+import TooltipLabel from '@/components/widgets/TooltipLabel'
+import _ from 'lodash'
+
+export default {
+  name: 'ReinstallVM',
+  components: {
+    DiskOfferingSelection,
+    DiskSizeSelection,
+    TemplateIsoSelection,
+    TooltipLabel
+  },
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  inject: ['parentFetchData'],
+  data () {
+    return {
+      overrideDiskOffering: false,
+      overrideDiskSize: false,
+      expungeDisk: false,
+      selectedDiskOffering: {},
+      loading: {
+        templates: false,
+        diskOfferings: false
+      },
+      rootDiskSizeKey: 'details[0].rootdisksize',
+      minIopsKey: 'details[0].minIops',
+      maxIopsKey: 'details[0].maxIops',
+      rootdisksize: 0,
+      minIops: 0,
+      maxIops: 0,
+      templateFilter: [
+        'featured',
+        'community',
+        'selfexecutable',
+        'sharedexecutable'
+      ],
+      diskOffering: {},
+      diskOfferingCount: 0,
+      templateKey: 0
+    }
+  },
+  beforeCreate () {
+    this.apiParams = this.$getApiParams('restoreVirtualMachine')
+  },
+  created () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      this.fetchDiskOfferings({})
+      this.fetchAllTemplates()
+    },
+    closeAction () {
+      this.$emit('close-action')
+    },
+    handlerError (error) {
+      this.error = error
+    },
+    handleSubmit () {
+      const params = {
+        virtualmachineid: this.resource.id,
+        templateid: this.templateid
+      }
+      if (this.overrideDiskOffering) {
+        params.diskofferingid = this.diskOffering.id
+        if (this.diskOffering.iscustomized) {
+          params[this.rootDiskSizeKey] = this.rootdisksize
+        }
+        if (this.diskOffering.iscustomizediops) {
+          params[this.minIopsKey] = this.minIops
+          params[this.maxIopsKey] = this.maxIops
+        }
+      }
+      if (this.overrideDiskSize && this.overrideRootDiskSize) {
+        params.rootdisksize = this.overrideRootDiskSize
+      }
+      params.expunge = this.expungeDisk
+      api('restoreVirtualMachine', params).then(response => {
+        this.$pollJob({
+          jobId: response.restorevmresponse.jobid,
+          successMessage: this.$t('label.reinstall.vm') + ' ' + 
this.$t('label.success'),
+          successMethod: (result) => {
+            const vm = result.jobresult.virtualmachine || {}
+            const name = vm.displayname || vm.name || vm.id
+            if (result.jobstatus === 1 && vm.password) {
+              this.$notification.success({
+                message: `${this.$t('label.reinstall.vm')}: ` + name,
+                description: `${this.$t('label.password.reset.confirm')}: ` + 
vm.password,
+                duration: 0
+              })
+            }
+          },
+          errorMessage: this.$t('label.reinstall.vm') + ' ' + 
this.$t('label.failed'),
+          errorMethod: (result) => {
+            this.closeAction()
+          },
+          loadingMessage: this.$t('label.reinstall.vm') + ': ' + 
this.resource.name,
+          catchMessage: this.$t('error.fetching.async.job.result')
+        })
+      }).catch(error => {
+        this.$notifyError(error)
+        this.closeAction()
+      }).finally(() => {
+        this.closeAction()
+      })
+    },
+    fetchAllTemplates (params) {
+      const promises = []
+      const templates = {}
+      this.loading.templates = true
+      this.templateFilter.forEach((filter) => {
+        templates[filter] = { count: 0, template: [] }
+        promises.push(this.fetchTemplates(filter, params))
+      })
+      this.templates = templates
+      Promise.all(promises).then((response) => {
+        response.forEach((resItem, idx) => {
+          templates[this.templateFilter[idx]] = 
_.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : 
resItem.listtemplatesresponse
+          this.templates = { ...templates }
+        })
+      }).catch((reason) => {
+        console.log(reason)
+      }).finally(() => {
+        this.loading.templates = false
+      })
+    },
+    fetchTemplates (templateFilter, params) {
+      const args = Object.assign({}, params)
+      if (args.keyword || args.category !== templateFilter) {
+        args.page = 1
+        args.pageSize = args.pageSize || 10
+      }
+      args.zoneid = _.get(this.zone, 'id')
+      args.templatefilter = templateFilter
+      args.details = 'all'
+      args.showicon = 'true'
+
+      return new Promise((resolve, reject) => {
+        api('listTemplates', args).then((response) => {
+          resolve(response)
+        }).catch((reason) => {
+          reject(reason)
+        })
+      })
+    },
+    fetchDiskOfferings (params) {
+      api('listDiskOfferings', { zoneid: this.resource.zoneid, listall: true, 
...params }).then((response) => {
+        this.diskOfferings = response?.listdiskofferingsresponse?.diskoffering 
|| []
+        this.diskOfferingCount = response?.listdiskofferingsresponse?.count || 0
+      })
+    },
+    onSelectDiskSize (rowSelected) {
+      this.diskOffering = rowSelected
+    },
+    updateFieldValue (input, value) {
+      this[input] = value
+    }
+  }
+}
+</script>
+
+<style
+  scoped
+  lang="scss"
+>
+.ant-form {
+  width: 90vw;
+
+  @media (min-width: 700px) {
+    width: 50vw;
+  }
+}
+</style>

Reply via email to