Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding root disk offering & size, and expunge old root disk while restoring a VM #8800

Merged
merged 14 commits into from
Apr 12, 2024
2 changes: 1 addition & 1 deletion api/src/main/java/com/cloud/vm/UserVmService.java
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ UserVm moveVMToUser(AssignVMCmd moveUserVMCmd) throws ResourceAllocationExceptio

UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException;

UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException;
UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException;

UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException,
VirtualMachineMigrationException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.apache.cloudstack.api.command.user.vm;

import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.log4j.Logger;

import org.apache.cloudstack.acl.SecurityChecker.AccessType;
Expand Down Expand Up @@ -60,6 +61,24 @@ 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).",
since = "4.19.1")
private Long rootDiskSize;

@Parameter(name = ApiConstants.EXPUNGE,
type = CommandType.BOOLEAN,
description = "Optional field to expunge old root volume after restore.",
since = "4.19.1")
private Boolean expungeRootDisk;

@Override
public String getEventType() {
return EventTypes.EVENT_VM_RESTORE;
Expand Down Expand Up @@ -112,6 +131,18 @@ public Long getId() {
return getVmId();
}

public Long getRootDiskOfferingId() {
return rootDiskOfferingId;
}

public Long getRootDiskSize() {
return rootDiskSize != null ? rootDiskSize * 1024L * 1024L * 1024L : null;
}

public Boolean getExpungeRootDisk() {
return expungeRootDisk != null && expungeRootDisk;
}

@Override
public Long getApiResourceId() {
return getId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ static String getHypervisorHostname(String name) {
*/
boolean unmanage(String vmUuid);

UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException;
UserVm restoreVirtualMachine(long vmId, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException;

boolean checkIfVmHasClusterWideVolumes(Long vmId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5623,20 +5623,20 @@
}

@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 Long rootDiskSize, final boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException {
final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
VmWorkJobVO placeHolder = null;
placeHolder = createPlaceHolderWork(vmId);
try {
return orchestrateRestoreVirtualMachine(vmId, newTemplateId);
return orchestrateRestoreVirtualMachine(vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge);

Check warning on line 5632 in engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java#L5632

Added line #L5632 was not covered by tests
} finally {
if (placeHolder != null) {
_workJobDao.expunge(placeHolder.getId());
}
}
} else {
final Outcome<VirtualMachine> outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId);
final Outcome<VirtualMachine> outcome = restoreVirtualMachineThroughJobQueue(vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge);

retrieveVmFromJobOutcome(outcome, String.valueOf(vmId), "restoreVirtualMachine");

Expand All @@ -5653,14 +5653,14 @@
}
}

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 Long rootDiskSize, final boolean expunge) throws ResourceUnavailableException, InsufficientCapacityException {
s_logger.debug("Restoring vm " + vmId + " with templateId : " + newTemplateId + " diskOfferingId : " + rootDiskOfferingId + " rootDiskSize: " + rootDiskSize);
final CallContext context = CallContext.current();
final Account account = context.getCallingAccount();
return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId);
return _userVmService.restoreVirtualMachine(account, vmId, newTemplateId, rootDiskOfferingId, rootDiskSize, expunge);
}

public Outcome<VirtualMachine> restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId) {
public Outcome<VirtualMachine> restoreVirtualMachineThroughJobQueue(final long vmId, final Long newTemplateId, final Long rootDiskOfferingId, final Long rootDiskSize, final boolean expunge) {
String commandName = VmWorkRestore.class.getName();
Pair<VmWorkJobVO, Long> pendingWorkJob = retrievePendingWorkJob(vmId, commandName);

Expand All @@ -5670,7 +5670,7 @@
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, rootDiskSize, expunge);

setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId);
}
Expand All @@ -5682,7 +5682,7 @@
@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.getRootDiskSize(), work.getExpunge());
HashMap<Long, String> passwordMap = new HashMap<>();
passwordMap.put(uservm.getId(), uservm.getPassword());
return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap));
Expand Down
25 changes: 19 additions & 6 deletions engine/orchestration/src/main/java/com/cloud/vm/VmWorkRestore.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,32 @@ public class VmWorkRestore extends VmWork {
private static final long serialVersionUID = 195901782359759635L;

private Long templateId;
private Long rootDiskOfferingId;
private Long rootDiskSize;

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, Long rootDiskSize, boolean expunge) {
super(vmWork);
this.templateId = templateId;
this.rootDiskOfferingId = rootDiskOfferingId;
this.rootDiskSize = rootDiskSize;
this.expunge = expunge;
}

public Long getTemplateId() {
return templateId;
}

public Long getRootDiskOfferingId() {
return rootDiskOfferingId;
}

public Long getRootDiskSize() {
return rootDiskSize;
}

public boolean getExpunge() {
return expunge;
}
}
58 changes: 47 additions & 11 deletions server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@
@Inject
private VmStatsDao vmStatsDao;
@Inject
private DataCenterDao dataCenterDao;
@Inject
private MessageBus messageBus;
@Inject
protected CommandSetupHelper commandSetupHelper;
Expand Down Expand Up @@ -3241,7 +3243,7 @@
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");
Expand Down Expand Up @@ -7642,27 +7644,52 @@

long vmId = cmd.getVmId();
Long newTemplateId = cmd.getTemplateId();
Long rootDiskOfferingId = cmd.getRootDiskOfferingId();
Long rootDiskSize = cmd.getRootDiskSize();
boolean expunge = cmd.getExpungeRootDisk();

if (rootDiskSize != null && rootDiskSize < 0) {
throw new InvalidParameterValueException("Invalid disk size " + rootDiskSize);

Check warning on line 7652 in server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L7652

Added line #L7652 was not covered by tests
}

UserVmVO vm = _vmDao.findById(vmId);
if (vm == null) {
InvalidParameterValueException ex = new InvalidParameterValueException("Cannot find VM with ID " + vmId);
ex.addProxyObject(String.valueOf(vmId), "vmId");
throw ex;
}

_accountMgr.checkAccess(caller, null, true, vm);

DiskOffering diskOffering;
if (rootDiskOfferingId != null) {
diskOffering = _diskOfferingDao.findById(rootDiskOfferingId);
if (diskOffering == null) {
throw new InvalidParameterValueException("Cannot find disk offering with ID " + rootDiskOfferingId);
}
DataCenterVO zone = dataCenterDao.findById(vm.getDataCenterId());
_accountMgr.checkAccess(caller, diskOffering, zone);
ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getServiceOfferingId());

Check warning on line 7671 in server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L7669-L7671

Added lines #L7669 - L7671 were not covered by tests
if (serviceOffering.getDiskOfferingStrictness() && !serviceOffering.getDiskOfferingId().equals(rootDiskOfferingId)) {
throw new InvalidParameterValueException("VM's service offering has a strict disk offering requirement, and the specified disk offering does not match");

Check warning on line 7673 in server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L7673

Added line #L7673 was not covered by tests
}
}
vishesh92 marked this conversation as resolved.
Show resolved Hide resolved

//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, rootDiskSize, expunge);
}

public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId, Long rootDiskOfferingId, Long rootDiskSize, boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException {
return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId, rootDiskOfferingId, rootDiskSize, expunge);
}

public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException {
return _itMgr.restoreVirtualMachine(vm.getId(), newTemplateId);

public UserVm restoreVMInternal(Account caller, UserVmVO vm) throws InsufficientCapacityException, ResourceUnavailableException {
return restoreVMInternal(caller, vm, null, null, null, false);
}

private VMTemplateVO getRestoreVirtualMachineTemplate(Account caller, Long newTemplateId, List<VolumeVO> rootVols, UserVmVO vm) {
Expand Down Expand Up @@ -7707,7 +7734,9 @@
}

@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 Long rootDiskSize,
final boolean expunge) throws InsufficientCapacityException, ResourceUnavailableException {
Long userId = caller.getId();
_userDao.findById(userId);
UserVmVO vm = _vmDao.findById(vmId);
Expand Down Expand Up @@ -7764,7 +7793,7 @@
}
}

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 ) {
final UserVmVO userVm = vm;
Expand Down Expand Up @@ -7797,15 +7826,22 @@
} else {
newVol = volumeMgr.allocateDuplicateVolume(root, null);
}
newVols.add(newVol);

VolumeVO resizedVolume = (VolumeVO) newVol;

if (userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE) == null && !newVol.getSize().equals(template.getSize())) {
VolumeVO resizedVolume = (VolumeVO) newVol;
if (template.getSize() != null) {
resizedVolume.setSize(template.getSize());
_volsDao.update(resizedVolume.getId(), resizedVolume);
}
}
if (diskOffering != null) {
resizedVolume.setDiskOfferingId(diskOffering.getId());
resizedVolume.setSize(diskOffering.getDiskSize());

Check warning on line 7839 in server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L7838-L7839

Added lines #L7838 - L7839 were not covered by tests
}
if (rootDiskSize != null) {
resizedVolume.setSize(rootDiskSize);

Check warning on line 7842 in server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L7842

Added line #L7842 was not covered by tests
}
_volsDao.update(resizedVolume.getId(), resizedVolume);
vishesh92 marked this conversation as resolved.
Show resolved Hide resolved

// 1. Save usage event and update resource count for user vm volumes
try {
Expand Down Expand Up @@ -7835,7 +7871,7 @@

// 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) {
Expand Down