-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Destroyvm also removes volumes #2793
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,12 +39,6 @@ | |
| import javax.inject.Inject; | ||
| import javax.naming.ConfigurationException; | ||
|
|
||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.apache.commons.collections.MapUtils; | ||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.apache.log4j.Logger; | ||
|
|
||
| import com.cloud.user.AccountVO; | ||
| import org.apache.cloudstack.acl.ControlledEntity.ACLType; | ||
| import org.apache.cloudstack.acl.SecurityChecker.AccessType; | ||
| import org.apache.cloudstack.affinity.AffinityGroupService; | ||
|
|
@@ -97,6 +91,10 @@ | |
| import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; | ||
| import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; | ||
| import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; | ||
| import org.apache.commons.codec.binary.Base64; | ||
| import org.apache.commons.collections.MapUtils; | ||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.apache.log4j.Logger; | ||
|
|
||
| import com.cloud.agent.AgentManager; | ||
| import com.cloud.agent.api.Answer; | ||
|
|
@@ -256,6 +254,7 @@ | |
| import com.cloud.user.Account; | ||
| import com.cloud.user.AccountManager; | ||
| import com.cloud.user.AccountService; | ||
| import com.cloud.user.AccountVO; | ||
| import com.cloud.user.ResourceLimitService; | ||
| import com.cloud.user.SSHKeyPair; | ||
| import com.cloud.user.SSHKeyPairVO; | ||
|
|
@@ -517,6 +516,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir | |
|
|
||
| private static final ConfigKey<Boolean> EnableAdditionalVmConfig = new ConfigKey<>("Advanced", Boolean.class, "enable.additional.vm.configuration", | ||
| "false", "allow additional arbitrary configuration to vm", true, ConfigKey.Scope.Account); | ||
| private static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false", | ||
| "On destroy, force-stop takes this value ", true); | ||
|
|
||
|
|
||
| @Override | ||
| public UserVmVO getVirtualMachine(long vmId) { | ||
|
|
@@ -2757,27 +2759,58 @@ public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, C | |
| // check if VM exists | ||
| UserVmVO vm = _vmDao.findById(vmId); | ||
|
|
||
| if (vm == null) { | ||
| if (vm == null || vm.getRemoved() != null) { | ||
| throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); | ||
| } | ||
|
|
||
| if (vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { | ||
| s_logger.debug("Vm id=" + vmId + " is already destroyed"); | ||
| return vm; | ||
| } | ||
|
|
||
| // check if there are active volume snapshots tasks | ||
| 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, vm destroy 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); | ||
|
|
||
| List<VolumeVO> volumes = getVolumesFromIds(cmd); | ||
|
|
||
| checkForUnattachedVolumes(vmId, volumes); | ||
| validateVolumes(volumes); | ||
|
|
||
| stopVirtualMachine(vmId, VmDestroyForcestop.value()); | ||
|
|
||
| detachVolumesFromVm(volumes); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we detach after the VM has been stopped? As the detach might not work, let's say libvirt (KVM) hangs or something else. If the VM is stopped the detach is a lot easier and you can remove them then.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't stop the VM for a regular detach cmd? This has been tested on KVM (with a small and large amount of volumes) and it worked fine. I'm happy to make the change if required though
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @houthuis Yes, I know. I was just thinking of it, what implications it might have. The stop of the VM just make sures the detach works even better, it saves additional commands to the Agent to detach the data volumes. This is because the KVM Agent needs to talk to libvirt to detach the disks.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wido Can you explain the risk here? It seems no different than a regular detach. Do these fail a lot?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DaanHoogland My point is that it's extra calls towards the KVM Agent, it needs to talk to libvirt, etc, etc. Since we are stopping the VM a few seconds later it would be best to do the stop first. This would make it less calls and thus less error prone.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wido this change has been made. please give the PR another review |
||
|
|
||
| UserVm destroyedVm = destroyVm(vmId, expunge); | ||
| if (expunge) { | ||
| if (!expunge(vm, ctx.getCallingUserId(), ctx.getCallingAccount())) { | ||
| throw new CloudRuntimeException("Failed to expunge vm " + destroyedVm); | ||
| } | ||
| } | ||
|
|
||
| deleteVolumesFromVm(volumes); | ||
|
|
||
| return destroyedVm; | ||
| } | ||
|
|
||
| private List<VolumeVO> getVolumesFromIds(DestroyVMCmd cmd) { | ||
| List<VolumeVO> volumes = new ArrayList<>(); | ||
| if (cmd.getVolumeIds() != null) { | ||
| for (Long volId : cmd.getVolumeIds()) { | ||
| VolumeVO vol = _volsDao.findById(volId); | ||
|
|
||
| if (vol == null) { | ||
| throw new InvalidParameterValueException("Unable to find volume with ID: " + volId); | ||
| } | ||
| volumes.add(vol); | ||
| } | ||
| } | ||
| return volumes; | ||
| } | ||
|
|
||
| @Override | ||
| @DB | ||
| public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { | ||
|
|
@@ -6518,4 +6551,52 @@ private boolean checkStatusOfVolumeSnapshots(long vmId, Volume.Type type) { | |
| } | ||
| return false; | ||
| } | ||
|
|
||
| private void checkForUnattachedVolumes(long vmId, List<VolumeVO> volumes) { | ||
|
|
||
| StringBuilder sb = new StringBuilder(); | ||
|
|
||
| for (VolumeVO volume : volumes) { | ||
| if (volume.getInstanceId() == null || vmId != volume.getInstanceId()) { | ||
| sb.append(volume.toString() + "; "); | ||
| } | ||
| } | ||
|
|
||
| if (!StringUtils.isEmpty(sb.toString())) { | ||
| throw new InvalidParameterValueException("The following supplied volumes are not attached to the VM: " + sb.toString()); | ||
| } | ||
| } | ||
|
|
||
| private void validateVolumes(List<VolumeVO> volumes) { | ||
|
|
||
| for (VolumeVO volume : volumes) { | ||
| if (!(volume.getVolumeType() == Volume.Type.ROOT || volume.getVolumeType() == Volume.Type.DATADISK)) { | ||
| throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK.toString() + " or " + Volume.Type.ROOT.toString()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void detachVolumesFromVm(List<VolumeVO> volumes) { | ||
|
|
||
| for (VolumeVO volume : volumes) { | ||
|
|
||
| Volume detachResult = _volumeService.detachVolumeViaDestroyVM(volume.getInstanceId(), volume.getId()); | ||
|
|
||
| if (detachResult == null) { | ||
| s_logger.error("DestroyVM remove volume - failed to detach and delete volume " + volume.getInstanceId() + " from instance " + volume.getId()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void deleteVolumesFromVm(List<VolumeVO> volumes) { | ||
|
|
||
| for (VolumeVO volume : volumes) { | ||
|
|
||
| boolean deleteResult = _volumeService.deleteVolume(volume.getId(), CallContext.current().getCallingAccount()); | ||
|
|
||
| if (!deleteResult) { | ||
| s_logger.error("DestroyVM remove volume - failed to delete volume " + volume.getInstanceId() + " from instance " + volume.getId()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove newline above, at line 728