Skip to content

Commit 237cda9

Browse files
committed
generate unmanage event and improve code coverage
1 parent a16fa51 commit 237cda9

File tree

6 files changed

+343
-36
lines changed

6 files changed

+343
-36
lines changed

api/src/main/java/org/apache/cloudstack/api/command/admin/vm/UnmanageVMInstanceCmd.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ public class UnmanageVMInstanceCmd extends BaseAsyncCmd {
6969

7070
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID,
7171
entityType = HostResponse.class, required = false,
72-
description = "ID of the host where domain XML is stored for stopped Instance",
72+
description = "ID of the host which will be used for unmanaging the Instance. " +
73+
"Applicable only for KVM hypervisor and stopped Instances. Domain XML will be stored on this host.",
7374
since = "4.22.0")
7475
private Long hostId;
7576

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,9 @@ public Boolean doInTransaction(TransactionStatus status) {
20492049
throw new CloudRuntimeException("Unable to retrieve host with ID: " + agentHostId);
20502050
}
20512051
logger.debug("Selected host UUID: {} to unmanage Instance: {}.", host.getUuid(), vm.getName());
2052+
ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, Domain.ROOT_DOMAIN, EventTypes.EVENT_VM_UNMANAGE,
2053+
String.format("Successfully unmanaged Instance: %s (ID: %s) on host ID: %s", vm.getName(), vm.getUuid(), host.getUuid()),
2054+
vm.getId(), ApiCommandResourceType.VirtualMachine.toString());
20522055
return new Pair<>(result, host.getUuid());
20532056
}
20542057

@@ -2092,14 +2095,14 @@ Long persistDomainForKVM(VMInstanceVO vm, Long paramHostId) {
20922095
/**
20932096
* Clean up VM snapshots (if any) from DB
20942097
*/
2095-
private void unmanageVMSnapshots(VMInstanceVO vm) {
2098+
void unmanageVMSnapshots(VMInstanceVO vm) {
20962099
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId(), true);
20972100
}
20982101

20992102
/**
21002103
* Clean up volumes for a VM to be unmanaged from CloudStack
21012104
*/
2102-
private void unmanageVMVolumes(VMInstanceVO vm) {
2105+
void unmanageVMVolumes(VMInstanceVO vm) {
21032106
final Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
21042107
if (hostId != null) {
21052108
volumeMgr.revokeAccess(vm.getId(), hostId);
@@ -2117,7 +2120,7 @@ private void unmanageVMVolumes(VMInstanceVO vm) {
21172120
* - If 'unmanage.vm.preserve.nics' = true: then the NICs are not removed but still Allocated, to preserve MAC addresses
21182121
* - If 'unmanage.vm.preserve.nics' = false: then the NICs are removed while unmanaging
21192122
*/
2120-
private void unmanageVMNics(VirtualMachineProfile profile, VMInstanceVO vm) {
2123+
void unmanageVMNics(VirtualMachineProfile profile, VMInstanceVO vm) {
21212124
logger.debug("Cleaning up NICs of {}.", vm.toString());
21222125
Boolean preserveNics = UnmanagedVMsManager.UnmanageVMPreserveNic.valueIn(vm.getDataCenterId());
21232126
if (BooleanUtils.isTrue(preserveNics)) {

engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.lang.reflect.Field;
4141
import java.util.ArrayList;
4242
import java.util.Arrays;
43+
import java.util.Collections;
44+
import java.util.Date;
4345
import java.util.HashMap;
4446
import java.util.List;
4547
import java.util.Map;
@@ -51,6 +53,10 @@
5153
import com.cloud.agent.api.UnmanageInstanceCommand;
5254
import com.cloud.agent.api.to.DataTO;
5355
import com.cloud.agent.api.to.DiskTO;
56+
import com.cloud.api.ApiDBUtils;
57+
import com.cloud.event.ActionEventUtils;
58+
import com.cloud.exception.ConcurrentOperationException;
59+
import com.cloud.ha.HighAvailabilityManager;
5460
import com.cloud.network.Network;
5561
import com.cloud.network.NetworkModel;
5662
import com.cloud.resource.ResourceManager;
@@ -64,6 +70,8 @@
6470
import org.apache.cloudstack.framework.extensions.dao.ExtensionDetailsDao;
6571
import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager;
6672
import org.apache.cloudstack.framework.extensions.vo.ExtensionDetailsVO;
73+
import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao;
74+
import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO;
6775
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
6876
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
6977
import org.apache.cloudstack.storage.to.VolumeObjectTO;
@@ -180,6 +188,9 @@ public class VirtualMachineManagerImplTest {
180188
private PrimaryDataStoreDao storagePoolDaoMock;
181189
@Mock
182190
private VMInstanceVO vmInstanceMock;
191+
@Mock
192+
private VmWorkJobDao _workJobDao;
193+
183194
private long vmInstanceVoMockId = 1L;
184195

185196
@Mock
@@ -194,6 +205,9 @@ public class VirtualMachineManagerImplTest {
194205
private long hostMockId = 1L;
195206
private long clusterMockId = 2L;
196207
private long zoneMockId = 3L;
208+
private final String vmMockUuid = UUID.randomUUID().toString();
209+
private final String hostUuid = UUID.randomUUID().toString();
210+
197211
@Mock
198212
private HostVO hostMock;
199213
@Mock
@@ -274,6 +288,10 @@ public class VirtualMachineManagerImplTest {
274288
VolumeDataFactory volumeDataFactoryMock;
275289
@Mock
276290
StorageManager storageManager;
291+
@Mock
292+
private HighAvailabilityManager _haMgr;
293+
@Mock
294+
VirtualMachineGuru guru;
277295

278296
private ConfigDepotImpl configDepotImpl;
279297
private boolean updatedConfigKeyDepot = false;
@@ -1813,6 +1831,14 @@ public void testPersistDomainForKvmAgentUnavailable() throws AgentUnavailableExc
18131831
assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage());
18141832
}
18151833

1834+
@Test(expected = ConcurrentOperationException.class)
1835+
public void testUnmanagePendingHaWork() {
1836+
when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock);
1837+
when(_workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, vmInstanceVoMockId)).thenReturn(Collections.emptyList());
1838+
when(_haMgr.hasPendingHaWork(vmInstanceVoMockId)).thenReturn(true);
1839+
virtualMachineManagerImpl.unmanage(vmMockUuid, null);
1840+
}
1841+
18161842
@Test
18171843
public void testPersistDomainForKvmOperationTimedOut() throws AgentUnavailableException, OperationTimedoutException {
18181844
when(vmInstanceMock.getState()).thenReturn(VirtualMachine.State.Running);
@@ -1821,4 +1847,110 @@ public void testPersistDomainForKvmOperationTimedOut() throws AgentUnavailableEx
18211847
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, () -> virtualMachineManagerImpl.persistDomainForKVM(vmInstanceMock, null));
18221848
assertEquals("Failed to send command to persist domain XML for Instance: " + vmName + " on host ID: " + hostMockId, exception.getMessage());
18231849
}
1850+
1851+
@Test(expected = CloudRuntimeException.class)
1852+
public void testUnmanageVmRemoved() {
1853+
when(vmInstanceMock.getRemoved()).thenReturn(new Date());
1854+
when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock);
1855+
virtualMachineManagerImpl.unmanage(vmMockUuid, null);
1856+
}
1857+
1858+
@Test(expected = ConcurrentOperationException.class)
1859+
public void testUnmanagePendingWorkJobs() {
1860+
when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock);
1861+
List<VmWorkJobVO> pendingJobs = new ArrayList<>();
1862+
VmWorkJobVO vmWorkJobVO = mock(VmWorkJobVO.class);
1863+
pendingJobs.add(vmWorkJobVO);
1864+
when(_workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, vmInstanceVoMockId)).thenReturn(pendingJobs);
1865+
virtualMachineManagerImpl.unmanage(vmMockUuid, null);
1866+
}
1867+
1868+
@Test(expected = CloudRuntimeException.class)
1869+
public void testUnmanageHostNotFoundAfterTransaction() {
1870+
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
1871+
when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock);
1872+
when(_workJobDao.listPendingWorkJobs(any(), anyLong())).thenReturn(Collections.emptyList());
1873+
when(_haMgr.hasPendingHaWork(anyLong())).thenReturn(false);
1874+
doReturn(guru).when(virtualMachineManagerImpl).getVmGuru(vmInstanceMock);
1875+
doNothing().when(virtualMachineManagerImpl).unmanageVMSnapshots(vmInstanceMock);
1876+
doNothing().when(virtualMachineManagerImpl).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class));
1877+
doNothing().when(virtualMachineManagerImpl).unmanageVMVolumes(vmInstanceMock);
1878+
doNothing().when(guru).finalizeUnmanage(vmInstanceMock);
1879+
try (MockedStatic<ApiDBUtils> ignored = Mockito.mockStatic(ApiDBUtils.class)) {
1880+
when(ApiDBUtils.findHostById(hostMockId)).thenReturn(null);
1881+
virtualMachineManagerImpl.unmanage(vmMockUuid, null);
1882+
}
1883+
}
1884+
1885+
@Test
1886+
public void testUnmanageSuccessNonKvm() {
1887+
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
1888+
when(hostMock.getUuid()).thenReturn(hostUuid);
1889+
when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock);
1890+
when(_workJobDao.listPendingWorkJobs(any(), anyLong())).thenReturn(Collections.emptyList());
1891+
when(_haMgr.hasPendingHaWork(anyLong())).thenReturn(false);
1892+
doReturn(guru).when(virtualMachineManagerImpl).getVmGuru(vmInstanceMock);
1893+
doNothing().when(virtualMachineManagerImpl).unmanageVMSnapshots(vmInstanceMock);
1894+
doNothing().when(virtualMachineManagerImpl).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class));
1895+
doNothing().when(virtualMachineManagerImpl).unmanageVMVolumes(vmInstanceMock);
1896+
doNothing().when(guru).finalizeUnmanage(vmInstanceMock);
1897+
try (MockedStatic<ApiDBUtils> ignored = Mockito.mockStatic(ApiDBUtils.class)) {
1898+
when(ApiDBUtils.findHostById(hostMockId)).thenReturn(hostMock);
1899+
try (MockedStatic<ActionEventUtils> actionUtil = Mockito.mockStatic(ActionEventUtils.class)) {
1900+
actionUtil.when(() -> ActionEventUtils.onActionEvent(
1901+
anyLong(), anyLong(), anyLong(),
1902+
anyString(), anyString(),
1903+
anyLong(), anyString()
1904+
)).thenReturn(1L);
1905+
1906+
Pair<Boolean, String> result = virtualMachineManagerImpl.unmanage(vmMockUuid, null);
1907+
assertNotNull(result);
1908+
assertTrue(result.first());
1909+
assertEquals(hostUuid, result.second());
1910+
1911+
verify(virtualMachineManagerImpl, never()).persistDomainForKVM(any(VMInstanceVO.class), anyLong());
1912+
verify(virtualMachineManagerImpl, times(1)).unmanageVMSnapshots(vmInstanceMock);
1913+
verify(virtualMachineManagerImpl, times(1)).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class));
1914+
verify(virtualMachineManagerImpl, times(1)).unmanageVMVolumes(vmInstanceMock);
1915+
verify(guru, times(1)).finalizeUnmanage(vmInstanceMock);
1916+
}
1917+
}
1918+
}
1919+
1920+
@Test
1921+
public void testUnmanageSuccessKvm() throws Exception {
1922+
when(vmInstanceMock.getHostId()).thenReturn(hostMockId);
1923+
when(hostMock.getUuid()).thenReturn(hostUuid);
1924+
when(vmInstanceMock.getHypervisorType()).thenReturn(HypervisorType.KVM);
1925+
when(vmInstanceDaoMock.findByUuid(vmMockUuid)).thenReturn(vmInstanceMock);
1926+
when(_workJobDao.listPendingWorkJobs(any(), anyLong())).thenReturn(Collections.emptyList());
1927+
when(_haMgr.hasPendingHaWork(anyLong())).thenReturn(false);
1928+
doReturn(guru).when(virtualMachineManagerImpl).getVmGuru(vmInstanceMock);
1929+
doNothing().when(virtualMachineManagerImpl).unmanageVMSnapshots(vmInstanceMock);
1930+
doNothing().when(virtualMachineManagerImpl).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class));
1931+
doNothing().when(virtualMachineManagerImpl).unmanageVMVolumes(vmInstanceMock);
1932+
doNothing().when(guru).finalizeUnmanage(vmInstanceMock);
1933+
try (MockedStatic<ApiDBUtils> ignored = Mockito.mockStatic(ApiDBUtils.class)) {
1934+
when(ApiDBUtils.findHostById(hostMockId)).thenReturn(hostMock);
1935+
try (MockedStatic<ActionEventUtils> actionUtil = Mockito.mockStatic(ActionEventUtils.class)) {
1936+
actionUtil.when(() -> ActionEventUtils.onActionEvent(
1937+
anyLong(), anyLong(), anyLong(),
1938+
anyString(), anyString(),
1939+
anyLong(), anyString()
1940+
)).thenReturn(1L);
1941+
UnmanageInstanceAnswer successAnswer = new UnmanageInstanceAnswer(null, true, "success");
1942+
when(agentManagerMock.send(anyLong(), any(UnmanageInstanceCommand.class))).thenReturn(successAnswer);
1943+
Pair<Boolean, String> result = virtualMachineManagerImpl.unmanage(vmMockUuid, null);
1944+
assertNotNull(result);
1945+
assertTrue(result.first());
1946+
assertEquals(hostUuid, result.second());
1947+
verify(virtualMachineManagerImpl, times(1)).persistDomainForKVM(vmInstanceMock, null);
1948+
verify(virtualMachineManagerImpl, times(1)).unmanageVMSnapshots(vmInstanceMock);
1949+
verify(virtualMachineManagerImpl, times(1)).unmanageVMNics(any(VirtualMachineProfile.class), any(VMInstanceVO.class));
1950+
verify(virtualMachineManagerImpl, times(1)).unmanageVMVolumes(vmInstanceMock);
1951+
verify(guru, times(1)).finalizeUnmanage(vmInstanceMock);
1952+
}
1953+
}
1954+
}
1955+
18241956
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9671,20 +9671,20 @@ public UserVm restoreVMFromBackup(CreateVMFromBackupCmd cmd) throws ResourceUnav
96719671
/*
96729672
Generate usage events related to unmanaging a VM
96739673
*/
9674-
private void publishUnmanageVMUsageEvents(UserVmVO vm, List<VolumeVO> volumes) {
9674+
void publishUnmanageVMUsageEvents(UserVmVO vm, List<VolumeVO> volumes) {
96759675
postProcessingUnmanageVMVolumes(volumes, vm);
96769676
postProcessingUnmanageVM(vm);
96779677
}
96789678

96799679
/*
96809680
Cleanup the VM from resources and groups
96819681
*/
9682-
private void cleanupUnmanageVMResources(UserVmVO vm) {
9682+
void cleanupUnmanageVMResources(UserVmVO vm) {
96839683
cleanupVmResources(vm);
96849684
removeVMFromAffinityGroups(vm.getId());
96859685
}
96869686

9687-
private void unmanageVMFromDB(long vmId) {
9687+
void unmanageVMFromDB(long vmId) {
96889688
VMInstanceVO vm = _vmInstanceDao.findById(vmId);
96899689
vmInstanceDetailsDao.removeDetails(vmId);
96909690
vm.setState(State.Expunging);
@@ -9746,15 +9746,15 @@ private void postProcessingUnmanageVMVolumes(List<VolumeVO> volumes, UserVmVO vm
97469746
}
97479747
}
97489748

9749-
private void checkUnmanagingVMOngoingVolumeSnapshots(UserVmVO vm) {
9749+
void checkUnmanagingVMOngoingVolumeSnapshots(UserVmVO vm) {
97509750
logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM {}", vm);
97519751
if (checkStatusOfVolumeSnapshots(vm, Volume.Type.ROOT)) {
97529752
throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on ROOT volume, vm unmanage is not permitted, please try again later.");
97539753
}
97549754
logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm {}", vm);
97559755
}
97569756

9757-
private void checkUnmanagingVMVolumes(UserVmVO vm, List<VolumeVO> volumes) {
9757+
void checkUnmanagingVMVolumes(UserVmVO vm, List<VolumeVO> volumes) {
97589758
for (VolumeVO volume : volumes) {
97599759
if (volume.getInstanceId() == null || !volume.getInstanceId().equals(vm.getId())) {
97609760
throw new CloudRuntimeException(String.format("Invalid state for volume %s of VM %s: it is not attached to VM", volume, vm));

0 commit comments

Comments
 (0)