Merge remote-tracking branch 'apache/4.19'

This commit is contained in:
Wei Zhou 2024-04-12 16:40:07 +02:00
commit 45daa1ce59
No known key found for this signature in database
GPG Key ID: 1503DFE7C8226103
14 changed files with 542 additions and 135 deletions

View File

@ -175,6 +175,8 @@ public interface VolumeApiService {
boolean validateVolumeSizeInBytes(long size); boolean validateVolumeSizeInBytes(long size);
void validateDestroyVolume(Volume volume, Account caller, boolean expunge, boolean forceExpunge);
Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException; Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException;
void publishVolumeCreationUsageEvent(Volume volume); void publishVolumeCreationUsageEvent(Volume volume);

View File

@ -492,7 +492,7 @@ public interface UserVmService {
UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; 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, UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException,
VirtualMachineMigrationException; VirtualMachineMigrationException;

View File

@ -16,9 +16,9 @@
// under the License. // under the License.
package org.apache.cloudstack.api.command.user.vm; package org.apache.cloudstack.api.command.user.vm;
import org.apache.cloudstack.api.ApiCommandResourceType; import com.cloud.vm.VmDetailConstants;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
@ -28,6 +28,7 @@ import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
@ -41,6 +42,8 @@ import com.cloud.user.Account;
import com.cloud.uservm.UserVm; import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine; 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}, @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, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = true) responseHasSensitiveInfo = true)
@ -58,6 +61,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") 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; 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 @Override
public String getEventType() { public String getEventType() {
return EventTypes.EVENT_VM_RESTORE; return EventTypes.EVENT_VM_RESTORE;
@ -110,6 +135,22 @@ public class RestoreVMCmd extends BaseAsyncCmd implements UserCmd {
return getVmId(); 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 @Override
public Long getApiResourceId() { public Long getApiResourceId() {
return getId(); return getId();

View File

@ -254,7 +254,7 @@ public interface VirtualMachineManager extends Manager {
*/ */
boolean unmanage(String vmUuid); 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); boolean checkIfVmHasClusterWideVolumes(Long vmId);

View File

@ -5692,20 +5692,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
@Override @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(); final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
VmWorkJobVO placeHolder = null; VmWorkJobVO placeHolder = null;
placeHolder = createPlaceHolderWork(vmId); placeHolder = createPlaceHolderWork(vmId);
try { try {
return orchestrateRestoreVirtualMachine(vmId, newTemplateId); return orchestrateRestoreVirtualMachine(vmId, newTemplateId, rootDiskOfferingId, expunge, details);
} finally { } finally {
if (placeHolder != null) { if (placeHolder != null) {
_workJobDao.expunge(placeHolder.getId()); _workJobDao.expunge(placeHolder.getId());
} }
} }
} else { } else {
final Outcome<VirtualMachine> outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId); final Outcome<VirtualMachine> outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, rootDiskOfferingId, expunge, details);
retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), "restoreVirtualMachine"); retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), "restoreVirtualMachine");
@ -5722,14 +5722,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
} }
private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException { private UserVm orchestrateRestoreVirtualMachine(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final boolean expunge, final Map<String, String> details) throws ResourceUnavailableException, InsufficientCapacityException {
logger.debug("Restoring vm " + vmId + " with new templateId " + newTemplateId); logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " details : " + details);
final CallContext context = CallContext.current(); final CallContext context = CallContext.current();
final Account account = context.getCallingAccount(); 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(); String commandName = VmWorkRestore.class.getName();
Pair<VmWorkJobVO, Long> pendingWorkJob = retrievePendingWorkJob(vmId, commandName); Pair<VmWorkJobVO, Long> pendingWorkJob = retrievePendingWorkJob(vmId, commandName);
@ -5739,7 +5739,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
Pair<VmWorkJobVO, VmWork> newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId); Pair<VmWorkJobVO, VmWork> newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId);
workJob = newVmWorkJobAndInfo.first(); workJob = newVmWorkJobAndInfo.first();
VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId); VmWorkRestore workInfo = new VmWorkRestore(newVmWorkJobAndInfo.second(), newTemplateId, rootDiskOfferingId, expunge, details);
setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId);
} }
@ -5751,7 +5751,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@ReflectionUse @ReflectionUse
private Pair<JobInfo.Status, String> orchestrateRestoreVirtualMachine(final VmWorkRestore work) throws Exception { private Pair<JobInfo.Status, String> orchestrateRestoreVirtualMachine(final VmWorkRestore work) throws Exception {
VMInstanceVO vm = findVmById(work.getVmId()); 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<>(); HashMap<Long, String> passwordMap = new HashMap<>();
passwordMap.put(uservm.getId(), uservm.getPassword()); passwordMap.put(uservm.getId(), uservm.getPassword());
return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap)); return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap));

View File

@ -16,23 +16,38 @@
// under the License. // under the License.
package com.cloud.vm; package com.cloud.vm;
import java.util.Map;
public class VmWorkRestore extends VmWork { public class VmWorkRestore extends VmWork {
private static final long serialVersionUID = 195901782359759635L; private static final long serialVersionUID = 195901782359759635L;
private Long templateId; private Long templateId;
private Long rootDiskOfferingId;
private Map<String,String> details;
public VmWorkRestore(long userId, long accountId, long vmId, String handlerName, Long templateId) { private boolean expunge;
super(userId, accountId, vmId, handlerName);
this.templateId = templateId; public VmWorkRestore(VmWork vmWork, Long templateId, Long rootDiskOfferingId, boolean expunge, Map<String,String> details) {
}
public VmWorkRestore(VmWork vmWork, Long templateId) {
super(vmWork); super(vmWork);
this.templateId = templateId; this.templateId = templateId;
this.rootDiskOfferingId = rootDiskOfferingId;
this.expunge = expunge;
this.details = details;
} }
public Long getTemplateId() { public Long getTemplateId() {
return templateId; return templateId;
} }
public Long getRootDiskOfferingId() {
return rootDiskOfferingId;
}
public boolean getExpunge() {
return expunge;
}
public Map<String, String> getDetails() {
return details;
}
} }

View File

@ -61,6 +61,9 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao; 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 @Component
public class CloudOrchestrator implements OrchestrationService { public class CloudOrchestrator implements OrchestrationService {
@ -196,8 +199,8 @@ public class CloudOrchestrator implements OrchestrationService {
Map<String, String> userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); Map<String, String> userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId());
if (userVmDetails != null) { if (userVmDetails != null) {
String minIops = userVmDetails.get("minIops"); String minIops = userVmDetails.get(MIN_IOPS);
String maxIops = userVmDetails.get("maxIops"); String maxIops = userVmDetails.get(MAX_IOPS);
rootDiskOfferingInfo.setMinIops(minIops != null && minIops.trim().length() > 0 ? Long.parseLong(minIops) : null); rootDiskOfferingInfo.setMinIops(minIops != null && minIops.trim().length() > 0 ? Long.parseLong(minIops) : null);
rootDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null); rootDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null);

View File

@ -879,32 +879,6 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
return diskProfile; return diskProfile;
} }
@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);
}
}
private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,
Account owner, long deviceId, String configurationId) { Account owner, long deviceId, String configurationId) {
assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template."; assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template.";
@ -948,18 +922,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
vol = _volsDao.persist(vol); vol = _volsDao.persist(vol);
List<VolumeDetailVO> volumeDetailsVO = new ArrayList<VolumeDetailVO>(); saveVolumeDetails(offering.getId(), vol.getId());
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);
}
if (StringUtils.isNotBlank(configurationId)) { if (StringUtils.isNotBlank(configurationId)) {
VolumeDetailVO deployConfigurationDetail = new VolumeDetailVO(vol.getId(), VmDetailConstants.DEPLOY_AS_IS_CONFIGURATION, configurationId, false); VolumeDetailVO deployConfigurationDetail = new VolumeDetailVO(vol.getId(), VmDetailConstants.DEPLOY_AS_IS_CONFIGURATION, configurationId, false);
@ -983,6 +946,32 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
return toDiskProfile(vol, offering); 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) @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating ROOT volume", create = true)
@Override @Override
public List<DiskProfile> allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, public List<DiskProfile> allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,

View File

@ -1723,11 +1723,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return _volStateMachine.transitTo(vol, event, null, _volsDao); return _volStateMachine.transitTo(vol, event, null, _volsDao);
} }
@Override public void validateDestroyVolume(Volume volume, Account caller, boolean expunge, boolean forceExpunge) {
@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);
if (expunge) { if (expunge) {
// When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVolume is false for the caller. // 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(); final Long userId = caller.getAccountId();
@ -1737,6 +1733,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
} else if (volume.getState() == Volume.State.Allocated || volume.getState() == Volume.State.Uploaded) { } 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"); 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); destroyVolumeIfPossible(volume);

View File

@ -18,6 +18,8 @@ package com.cloud.vm;
import static com.cloud.configuration.ConfigurationManagerImpl.VM_USERDATA_MAX_LENGTH; import static com.cloud.configuration.ConfigurationManagerImpl.VM_USERDATA_MAX_LENGTH;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize; 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.IOException;
import java.io.StringReader; import java.io.StringReader;
@ -568,6 +570,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Inject @Inject
private VmStatsDao vmStatsDao; private VmStatsDao vmStatsDao;
@Inject @Inject
private DataCenterDao dataCenterDao;
@Inject
private MessageBus messageBus; private MessageBus messageBus;
@Inject @Inject
protected CommandSetupHelper commandSetupHelper; protected CommandSetupHelper commandSetupHelper;
@ -2194,11 +2198,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Long maxIopsInNewDiskOffering = null; Long maxIopsInNewDiskOffering = null;
boolean autoMigrate = false; boolean autoMigrate = false;
boolean shrinkOk = false; boolean shrinkOk = false;
if (customParameters.containsKey(ApiConstants.MIN_IOPS)) { if (customParameters.containsKey(MIN_IOPS)) {
minIopsInNewDiskOffering = Long.parseLong(customParameters.get(ApiConstants.MIN_IOPS)); minIopsInNewDiskOffering = Long.parseLong(customParameters.get(MIN_IOPS));
} }
if (customParameters.containsKey(ApiConstants.MAX_IOPS)) { if (customParameters.containsKey(MAX_IOPS)) {
minIopsInNewDiskOffering = Long.parseLong(customParameters.get(ApiConstants.MAX_IOPS)); minIopsInNewDiskOffering = Long.parseLong(customParameters.get(MAX_IOPS));
} }
if (customParameters.containsKey(ApiConstants.AUTO_MIGRATE)) { if (customParameters.containsKey(ApiConstants.AUTO_MIGRATE)) {
autoMigrate = Boolean.parseBoolean(customParameters.get(ApiConstants.AUTO_MIGRATE)); autoMigrate = Boolean.parseBoolean(customParameters.get(ApiConstants.AUTO_MIGRATE));
@ -3301,7 +3305,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
ServiceOfferingVO offering = serviceOfferingDao.findById(vmInstance.getId(), serviceOfferingId); ServiceOfferingVO offering = serviceOfferingDao.findById(vmInstance.getId(), serviceOfferingId);
if (offering != null && offering.getRemoved() == null) { if (offering != null && offering.getRemoved() == null) {
if (offering.isVolatileVm()) { if (offering.isVolatileVm()) {
return restoreVMInternal(caller, vmInstance, null); return restoreVMInternal(caller, vmInstance);
} }
} else { } else {
throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId + " corresponding to the vm"); throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId + " corresponding to the vm");
@ -6451,8 +6455,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// if specified, minIops should be <= maxIops // if specified, minIops should be <= maxIops
private void verifyDetails(Map<String,String> details) { private void verifyDetails(Map<String,String> details) {
if (details != null) { if (details != null) {
String minIops = details.get("minIops"); String minIops = details.get(MIN_IOPS);
String maxIops = details.get("maxIops"); String maxIops = details.get(MAX_IOPS);
verifyMinAndMaxIops(minIops, maxIops); verifyMinAndMaxIops(minIops, maxIops);
@ -7811,6 +7815,20 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return false; 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 @Override
public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException { public UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException {
// Input validation // Input validation
@ -7818,6 +7836,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
long vmId = cmd.getVmId(); long vmId = cmd.getVmId();
Long newTemplateId = cmd.getTemplateId(); 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); UserVmVO vm = _vmDao.findById(vmId);
if (vm == null) { if (vm == null) {
@ -7825,20 +7848,38 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
ex.addProxyObject(String.valueOf(vmId), "vmId"); ex.addProxyObject(String.valueOf(vmId), "vmId");
throw ex; throw ex;
} }
_accountMgr.checkAccess(caller, null, true, vm); _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 //check if there are any active snapshots on volumes associated with the VM
logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vmId); logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vmId);
if (checkStatusOfVolumeSnapshots(vmId, Volume.Type.ROOT)) { 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."); throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on ROOT volume, Re-install VM is not permitted, please try again later.");
} }
logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm with id " + vmId); logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm with id " + vmId);
return restoreVMInternal(caller, vm, newTemplateId); return restoreVMInternal(caller, vm, newTemplateId, rootDiskOfferingId, expunge, details);
} }
public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException { 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); 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) { private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List<VolumeVO> rootVols, UserVmVO vm) {
@ -7883,7 +7924,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
} }
@Override @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(); Long userId = caller.getId();
_userDao.findById(userId); _userDao.findById(userId);
UserVmVO vm = _vmDao.findById(vmId); UserVmVO vm = _vmDao.findById(vmId);
@ -7940,9 +7983,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) { for (VolumeVO root : rootVols) {
if ( !Volume.State.Allocated.equals(root.getState()) || newTemplateId != null ) { if ( !Volume.State.Allocated.equals(root.getState()) || newTemplateId != null ) {
_volumeService.validateDestroyVolume(root, caller, expunge, false);
final UserVmVO userVm = vm; final UserVmVO userVm = vm;
Pair<UserVmVO, Volume> vmAndNewVol = Transaction.execute(new TransactionCallbackWithException<Pair<UserVmVO, Volume>, CloudRuntimeException>() { Pair<UserVmVO, Volume> vmAndNewVol = Transaction.execute(new TransactionCallbackWithException<Pair<UserVmVO, Volume>, CloudRuntimeException>() {
@Override @Override
@ -7973,15 +8017,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
} else { } else {
newVol = volumeMgr.allocateDuplicateVolume(root, null); newVol = volumeMgr.allocateDuplicateVolume(root, null);
} }
newVols.add(newVol);
if (userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !newVol.getSize().equals(template.getSize())) { updateVolume(newVol, template, userVm, diskOffering, details);
VolumeVO resizedVolume = (VolumeVO) newVol; volumeMgr.saveVolumeDetails(newVol.getDiskOfferingId(), newVol.getId());
if (template.getSize() != null) {
resizedVolume.setSize(template.getSize());
_volsDao.update(resizedVolume.getId(), resizedVolume);
}
}
// 1. Save usage event and update resource count for user vm volumes // 1. Save usage event and update resource count for user vm volumes
try { try {
@ -8010,7 +8048,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// Detach, destroy and create the usage event for the old root volume. // Detach, destroy and create the usage event for the old root volume.
_volsDao.detachVolume(root.getId()); _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 // 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) { if (vm.getHypervisorType() == HypervisorType.VMware) {
@ -8073,6 +8111,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) { private void updateVMDynamicallyScalabilityUsingTemplate(UserVmVO vm, Long newTemplateId) {
ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId()); ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId());
VMTemplateVO newTemplate = _templateDao.findById(newTemplateId); VMTemplateVO newTemplate = _templateDao.findById(newTemplateId);

View File

@ -185,6 +185,9 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; 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 class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
public static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso"; 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"; public static final String KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME = "kvm-default-vm-import-dummy-template";
@ -1200,12 +1203,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId()));
} }
Long minIops = null; Long minIops = null;
if (details.containsKey("minIops")) { if (details.containsKey(MIN_IOPS)) {
minIops = Long.parseLong(details.get("minIops")); minIops = Long.parseLong(details.get(MIN_IOPS));
} }
Long maxIops = null; Long maxIops = null;
if (details.containsKey("maxIops")) { if (details.containsKey(MAX_IOPS)) {
maxIops = Long.parseLong(details.get("maxIops")); maxIops = Long.parseLong(details.get(MAX_IOPS));
} }
DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId());
diskProfileStoragePoolList.add(importDisk(rootDisk, userVm, cluster, diskOffering, Volume.Type.ROOT, String.format("ROOT-%d", userVm.getId()), diskProfileStoragePoolList.add(importDisk(rootDisk, userVm, cluster, diskOffering, Volume.Type.ROOT, String.format("ROOT-%d", userVm.getId()),

View File

@ -97,8 +97,6 @@ import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO; import com.cloud.storage.GuestOSVO;
import com.cloud.storage.ScopeType; import com.cloud.storage.ScopeType;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
@ -1321,18 +1319,6 @@ public class UserVmManagerImplTest {
when(cmd.getTemplateId()).thenReturn(2L); when(cmd.getTemplateId()).thenReturn(2L);
when(userVmDao.findById(vmId)).thenReturn(userVmVoMock); 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); userVmManagerImpl.restoreVM(cmd);
} }
@ -1346,7 +1332,7 @@ public class UserVmManagerImplTest {
when(userVmVoMock.getAccountId()).thenReturn(accountId); when(userVmVoMock.getAccountId()).thenReturn(accountId);
when(accountDao.findById(accountId)).thenReturn(null); when(accountDao.findById(accountId)).thenReturn(null);
userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null);
} }
@Test(expected = PermissionDeniedException.class) @Test(expected = PermissionDeniedException.class)
@ -1360,7 +1346,7 @@ public class UserVmManagerImplTest {
when(accountDao.findById(accountId)).thenReturn(callerAccount); when(accountDao.findById(accountId)).thenReturn(callerAccount);
when(callerAccount.getState()).thenReturn(Account.State.DISABLED); when(callerAccount.getState()).thenReturn(Account.State.DISABLED);
userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -1375,7 +1361,7 @@ public class UserVmManagerImplTest {
when(accountDao.findById(accountId)).thenReturn(callerAccount); when(accountDao.findById(accountId)).thenReturn(callerAccount);
when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting); when(userVmVoMock.getState()).thenReturn(VirtualMachine.State.Starting);
userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null);
} }
@Test(expected = InvalidParameterValueException.class) @Test(expected = InvalidParameterValueException.class)
@ -1396,7 +1382,7 @@ public class UserVmManagerImplTest {
when(templateDao.findById(currentTemplateId)).thenReturn(currentTemplate); when(templateDao.findById(currentTemplateId)).thenReturn(currentTemplate);
when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(new ArrayList<VolumeVO>()); 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) @Test(expected = InvalidParameterValueException.class)
@ -1423,7 +1409,7 @@ public class UserVmManagerImplTest {
volumes.add(rootVolume2); volumes.add(rootVolume2);
when(volumeDaoMock.findByInstanceAndType(vmId, Volume.Type.ROOT)).thenReturn(volumes); 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) @Test(expected = InvalidParameterValueException.class)
@ -1450,7 +1436,7 @@ public class UserVmManagerImplTest {
vmSnapshots.add(vmSnapshot); vmSnapshots.add(vmSnapshot);
when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots); when(vmSnapshotDaoMock.findByVm(vmId)).thenReturn(vmSnapshots);
userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId); userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null);
} }
@Test @Test

View File

@ -164,33 +164,10 @@ export default {
label: 'label.reinstall.vm', label: 'label.reinstall.vm',
message: 'message.reinstall.vm', message: 'message.reinstall.vm',
dataView: true, dataView: true,
args: ['virtualmachineid', 'templateid'], popup: true,
filters: (record) => {
var filters = {}
var filterParams = {}
filterParams.hypervisortype = record.hypervisor
filterParams.zoneid = record.zoneid
filters.templateid = filterParams
return filters
},
show: (record) => { return ['Running', 'Stopped'].includes(record.state) }, show: (record) => { return ['Running', 'Stopped'].includes(record.state) },
mapping: {
virtualmachineid: {
value: (record) => { return record.id }
}
},
disabled: (record) => { return record.hostcontrolstate === 'Offline' }, disabled: (record) => { return record.hostcontrolstate === 'Offline' },
successMethod: (obj, result) => { component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ReinstallVm.vue')))
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
})
}
}
}, },
{ {
api: 'createVMSnapshot', api: 'createVMSnapshot',

View 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>