mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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
This commit is contained in:
parent
d3e020a545
commit
b998e7dbb6
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
307
ui/src/views/compute/ReinstallVm.vue
Normal file
307
ui/src/views/compute/ReinstallVm.vue
Normal file
@ -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>
|
||||
Loading…
x
Reference in New Issue
Block a user