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 <[email protected]>
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>