This is an automated email from the ASF dual-hosted git repository. gutoveronezi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push: new 7c61d8aeaf Set root volume as destroyed when destroying a VM (#6868) 7c61d8aeaf is described below commit 7c61d8aeaff7ecd496ec6e7f4a6d2885a3dfe174 Author: João Jandre <48719461+joaojan...@users.noreply.github.com> AuthorDate: Tue Dec 6 17:48:35 2022 -0300 Set root volume as destroyed when destroying a VM (#6868) * Set root volume as destroyed when destroying a VM * Address review * Address review Co-authored-by: João Jandre <j...@scclouds.com.br> --- .../main/java/com/cloud/storage/dao/VolumeDao.java | 1 + .../java/com/cloud/storage/dao/VolumeDaoImpl.java | 7 +++++ .../java/com/cloud/storage/StorageManagerImpl.java | 12 +++++++++ .../src/main/java/com/cloud/vm/UserVmManager.java | 6 +++++ .../main/java/com/cloud/vm/UserVmManagerImpl.java | 30 +++++++++++++++++++--- .../java/com/cloud/vm/UserVmManagerImplTest.java | 18 +++++++++++++ 6 files changed, 71 insertions(+), 3 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java index 64151d6068..2001cf05ab 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java @@ -146,4 +146,5 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S * @return the list of volumes that uses that disk offering. */ List<VolumeVO> findByDiskOfferingId(long diskOfferingId); + VolumeVO getInstanceRootVolume(long instanceId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index d27faa3a78..3c865bac66 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -759,4 +759,11 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol throw new CloudRuntimeException(e); } } + @Override + public VolumeVO getInstanceRootVolume(long instanceId) { + SearchCriteria<VolumeVO> sc = RootDiskStateSearch.create(); + sc.setParameters("instanceId", instanceId); + sc.setParameters("vType", Volume.Type.ROOT); + return findOneBy(sc); + } } diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 00a15e6e32..0018cc49d1 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -46,6 +46,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import com.google.common.collect.Sets; +import com.cloud.vm.UserVmManager; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ApiConstants; @@ -344,6 +345,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C @Inject private AnnotationDao annotationDao; + @Inject + protected UserVmManager userVmManager; protected List<StoragePoolDiscoverer> _discoverers; public List<StoragePoolDiscoverer> getDiscoverers() { @@ -1325,6 +1328,15 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C List<VolumeVO> vols = _volsDao.listVolumesToBeDestroyed(new Date(System.currentTimeMillis() - ((long)StorageCleanupDelay.value() << 10))); for (VolumeVO vol : vols) { + if (Type.ROOT.equals(vol.getVolumeType())) { + VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vol.getInstanceId()); + if (vmInstanceVO != null && vmInstanceVO.getState() == State.Destroyed) { + s_logger.debug(String.format("ROOT volume [%s] will not be expunged because the VM is [%s], therefore this volume will be expunged with the VM" + + " cleanup job.", vol.getUuid(), vmInstanceVO.getState())); + continue; + } + } + try { // If this fails, just log a warning. It's ideal if we clean up the host-side clustered file // system, but not necessary. diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index 082c36f518..5029615b24 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -57,6 +57,10 @@ public interface UserVmManager extends UserVmService { ConfigKey<Boolean> DisplayVMOVFProperties = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.display.ovf.properties", "false", "Set display of VMs OVF properties as part of VM details", true); + ConfigKey<Boolean> DestroyRootVolumeOnVmDestruction = new ConfigKey<Boolean>("Advanced", Boolean.class, "destroy.root.volume.on.vm.destruction", "false", + "Destroys the VM's root volume when the VM is destroyed.", + true, ConfigKey.Scope.Domain); + static final int MAX_USER_DATA_LENGTH_BYTES = 2048; public static final String CKS_NODE = "cksnode"; @@ -136,4 +140,6 @@ public interface UserVmManager extends UserVmService { boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, ServiceOffering offering, VirtualMachineTemplate template, Long zoneId); + Boolean getDestroyRootVolumeOnVmDestruction(Long domainId); + } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 4e4f38e1af..caa2459f5a 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -596,6 +596,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private int _scaleRetry; private Map<Long, VmAndCountDetails> vmIdCountMap = new ConcurrentHashMap<>(); + protected static long ROOT_DEVICE_ID = 0; + private static final int MAX_HTTP_GET_LENGTH = 2 * MAX_USER_DATA_LENGTH_BYTES; private static final int NUM_OF_2K_BLOCKS = 512; private static final int MAX_HTTP_POST_LENGTH = NUM_OF_2K_BLOCKS * MAX_USER_DATA_LENGTH_BYTES; @@ -2333,8 +2335,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List<VolumeVO> volumes = _volsDao.findByInstance(vmId); for (VolumeVO volume : volumes) { if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - // Create an event - _volumeService.publishVolumeCreationUsageEvent(volume); + recoverRootVolume(volume, vmId); + break; } } @@ -2346,6 +2348,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return _vmDao.findById(vmId); } + protected void recoverRootVolume(VolumeVO volume, Long vmId) { + if (Volume.State.Destroy.equals(volume.getState())) { + _volumeService.recoverVolume(volume.getId()); + _volsDao.attachVolume(volume.getId(), vmId, ROOT_DEVICE_ID); + } else { + _volumeService.publishVolumeCreationUsageEvent(volume); + } + } + @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { _name = name; @@ -3330,6 +3341,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir deleteVolumesFromVm(volumesToBeDeleted, expunge); + if (getDestroyRootVolumeOnVmDestruction(vm.getDomainId())) { + VolumeVO rootVolume = _volsDao.getInstanceRootVolume(vm.getId()); + if (rootVolume != null) { + _volService.destroyVolume(rootVolume.getId()); + } else { + s_logger.warn(String.format("Tried to destroy ROOT volume for VM [%s], but couldn't retrieve it.", vm.getUuid())); + } + } + return destroyedVm; } @@ -8004,7 +8024,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir public ConfigKey<?>[] getConfigKeys() { return new ConfigKey<?>[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties, - KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList}; + KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction}; } @Override @@ -8334,4 +8354,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir s_logger.warn(String.format("Skip collecting vm %s disk and network statistics as the expected vm state is %s but actual state is %s", vm, expectedState, vm.getState())); } } + + public Boolean getDestroyRootVolumeOnVmDestruction(Long domainId){ + return DestroyRootVolumeOnVmDestruction.valueIn(domainId); + } } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 2e6f72e67b..06a56e45f1 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.vm; +import com.cloud.storage.Volume; +import com.cloud.storage.dao.VolumeDao; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -166,6 +168,12 @@ public class UserVmManagerImplTest { @Mock UserDataDao userDataDao; + @Mock + private VolumeVO volumeVOMock; + + @Mock + private VolumeDao volumeDaoMock; + private long vmId = 1l; private static final long GiB_TO_BYTES = 1024 * 1024 * 1024; @@ -856,4 +864,14 @@ public class UserVmManagerImplTest { Assert.assertEquals("testUserdata", userVmVO.getUserData()); Assert.assertEquals(1L, (long)userVmVO.getUserDataId()); } + + @Test + public void recoverRootVolumeTestDestroyState() { + Mockito.doReturn(Volume.State.Destroy).when(volumeVOMock).getState(); + + userVmManagerImpl.recoverRootVolume(volumeVOMock, vmId); + + Mockito.verify(volumeApiService).recoverVolume(volumeVOMock.getId()); + Mockito.verify(volumeDaoMock).attachVolume(volumeVOMock.getId(), vmId, UserVmManagerImpl.ROOT_DEVICE_ID); + } }