package org.ovirt.engine.core.vdsbroker.monitoring; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Objects; import org.junit.Before; import org.junit.Rule; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VDSStatus; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VdsDynamic; import org.ovirt.engine.core.common.businessentities.VmExitStatus; import org.ovirt.engine.core.common.businessentities.VmStatistics; import org.ovirt.engine.core.common.vdscommands.DestroyVmVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSParametersBase; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase; import org.ovirt.engine.core.dao.VdsDynamicDao; import org.ovirt.engine.core.di.InjectorRule; import org.ovirt.engine.core.vdsbroker.ResourceManager; import org.ovirt.engine.core.vdsbroker.VdsManager; import org.ovirt.engine.core.vdsbroker.VmManager; @RunWith(Theories.class) public class VmAnalyzerTest { @DataPoints public static VmTestPairs[] VMS = VmTestPairs.values(); VmAnalyzer vmAnalyzer; @Mock private AuditLogDirector auditLogDirector; @Captor private ArgumentCaptor<AuditLogableBase> loggableCaptor; @Captor private ArgumentCaptor<AuditLogType> logTypeCaptor; @Captor private ArgumentCaptor<VDSCommandType> vdsCommandTypeCaptor; @Captor private ArgumentCaptor<VDSParametersBase> vdsParamsCaptor; @Mock private VdsDynamicDao vdsDynamicDao; @Mock private VdsDynamic srcHost; @Mock private VdsDynamic dstHost; @Mock private VdsManager vdsManager; @Mock private VmManager vmManager; @Mock private VDS vdsManagerVds; @Mock private ResourceManager resourceManager; @Rule public InjectorRule injectorRule = new InjectorRule(); @Theory public void externalVMWhenMissingInDb(VmTestPairs data) { //given initMocks(data, false); //when assumeTrue(data.dbVm() == null); assumeTrue(data.vdsmVm() != null); //then vmAnalyzer.analyze(); assertTrue(vmAnalyzer.isUnmanagedVm()); } @Theory public void vmNotRunningOnHost(VmTestPairs data) { //given initMocks(data, false); //when assumeTrue(data.vdsmVm() == null); //then vmAnalyzer.analyze(); assertTrue(vmAnalyzer.isMovedToDown()); } @Theory public void proceedDownVmsNormalExistReason_MIGRATION_HANDOVER(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeTrue(data.dbVm().getStatus() == VMStatus.MigratingFrom); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); assumeTrue(data.vdsmVm().getVmDynamic().getExitStatus() == VmExitStatus.Normal); //then vmAnalyzer.analyze(); verify(resourceManager, never()).removeAsyncRunningVm(data.dbVm().getId()); verify(vmAnalyzer).runVdsCommand(vdsCommandTypeCaptor.capture(), vdsParamsCaptor.capture()); assertEquals(data.dbVm().getDynamicData(), vmAnalyzer.getVmDynamicToSave()); assertEquals(VDSCommandType.Destroy, vdsCommandTypeCaptor.getValue()); assertEquals(DestroyVmVDSCommandParameters.class, vdsParamsCaptor.getValue().getClass()); } @Theory public void proceedDownVmsNormalExistReason(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeTrue(data.dbVm().getStatus() != VMStatus.MigratingFrom); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); assumeTrue(data.vdsmVm().getVmDynamic().getExitStatus() == VmExitStatus.Normal); //then vmAnalyzer.analyze(); verify(auditLogDirector, atLeastOnce()).log(loggableCaptor.capture(), logTypeCaptor.capture()); verify(resourceManager).removeAsyncRunningVm(data.dbVm().getId()); verify(vmAnalyzer).runVdsCommand(vdsCommandTypeCaptor.capture(), vdsParamsCaptor.capture()); assertEquals(data.dbVm().getDynamicData(), vmAnalyzer.getVmDynamicToSave()); assertTrue(logTypeCaptor.getAllValues().contains(AuditLogType.VM_DOWN)); assertEquals(VDSCommandType.Destroy, vdsCommandTypeCaptor.getValue()); assertEquals(vdsParamsCaptor.getValue().getClass(), DestroyVmVDSCommandParameters.class); } @Theory public void proceedDownVmsErrorExitReason(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); assumeTrue(data.vdsmVm().getVmDynamic().getExitStatus() != VmExitStatus.Normal); //then vmAnalyzer.analyze(); verify(auditLogDirector, atLeastOnce()).log(loggableCaptor.capture(), logTypeCaptor.capture()); assertEquals(data.dbVm().getDynamicData(), vmAnalyzer.getVmDynamicToSave()); } @Theory public void proceedWatchdogEvents(VmTestPairs data) { //given initMocks(data, true); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeFalse(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); assumeTrue(Objects.equals(data.vdsmVm().getVmDynamic().getRunOnVds(), data.dbVm().getRunOnVds())); //then verify(auditLogDirector, atLeastOnce()).log(loggableCaptor.capture(), logTypeCaptor.capture()); assertTrue(logTypeCaptor.getAllValues().contains(AuditLogType.WATCHDOG_EVENT)); } @Theory public void proceedBalloonCheck(VmTestPairs data) { //given initMocks(data, true); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeFalse(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); assumeTrue(Objects.equals(data.vdsmVm().getVmDynamic().getRunOnVds(), data.dbVm().getRunOnVds())); //then verify(auditLogDirector, atLeastOnce()).log(loggableCaptor.capture(), logTypeCaptor.capture()); assertTrue(logTypeCaptor.getAllValues().contains(AuditLogType.WATCHDOG_EVENT)); } @Theory public void vmNotRunningOnHostWithBalloonEnabled(VmTestPairs data) { //given initMocks(data, false); when(vdsManagerVds.isBalloonEnabled()).thenReturn(true); //when assumeTrue(data.vdsmVm() == null); //then vmAnalyzer.analyze(); assertTrue(vmAnalyzer.isMovedToDown()); } @Theory public void proceedGuaranteedMemoryCheck() { //TODO add tests here } @Theory public void updateRepository_MIGRATION_FROM(VmTestPairs data) { //given initMocks(data, true); //when assumeNotNull(data.dbVm(), data.vdsmVm()); // when vm is migrating assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.MigratingFrom); //then verify(resourceManager, never()).internalSetVmStatus(data.dbVm().getDynamicData(), VMStatus.MigratingTo); } @Theory public void updateRepository_MIGRATION_FROM_TO_DOWN(VmTestPairs data) { //given initMocks(data, true); //when assumeNotNull(data.dbVm(), data.vdsmVm()); // when vm ended migration assumeTrue(data.dbVm().getStatus() == VMStatus.MigratingFrom); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); //then verify(resourceManager, times(1)).internalSetVmStatus(data.dbVm().getDynamicData(), VMStatus.MigratingTo); assertEquals(data.dbVm().getDynamicData(), vmAnalyzer.getVmDynamicToSave()); assertEquals(VmTestPairs.DST_HOST_ID, data.dbVm().getRunOnVds()); } @Theory public void updateRepository_MIGRATION_FROM_TO_UP(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.dbVm(), data.vdsmVm()); // when migration failed assumeTrue(data.dbVm().getStatus() == VMStatus.MigratingFrom); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Up); //then vmAnalyzer.analyze(); verify(resourceManager, times(1)).removeVmFromDownVms(VmTestPairs.SRC_HOST_ID, data.vdsmVm().getVmDynamic().getId()); assertEquals(data.dbVm().getDynamicData(), vmAnalyzer.getVmDynamicToSave()); assertEquals(VmTestPairs.SRC_HOST_ID, data.dbVm().getRunOnVds()); assertTrue(vmAnalyzer.isRerun()); assertNull(data.dbVm().getMigratingToVds()); } @Theory public void updateRepository_HA_VM_DOWN(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeTrue(data.dbVm().getStatus() == VMStatus.Up); assumeTrue(data.dbVm().isAutoStartup()); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Down); //then vmAnalyzer.analyze(); assertEquals(data.dbVm().getDynamicData(), vmAnalyzer.getVmDynamicToSave()); assertNotNull(vmAnalyzer.getVmStatisticsToSave()); assertFalse(vmAnalyzer.isRerun()); assertTrue(vmAnalyzer.isAutoVmToRun()); assertNull(data.dbVm().getMigratingToVds()); } @Theory public void updateRepository_PERSIST_DST_UP_VMS(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.vdsmVm()); assumeTrue(data.vdsmVm().getVmDynamic().getRunOnVds() == VmTestPairs.DST_HOST_ID); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.Up); //then vmAnalyzer.analyze(); assertNotNull(vmAnalyzer.getVmDynamicToSave()); assertNotEquals(data.vdsmVm().getVmDynamic(), vmAnalyzer.getVmDynamicToSave()); } @Theory public void updateRepository_PERSIST_ALL_VMS_EXCEPT_MIGRATING_TO(VmTestPairs data) { //given initMocks(data, false); //when assumeNotNull(data.dbVm(), data.vdsmVm()); assumeTrue(data.vdsmVm().getVmDynamic().getRunOnVds() == VmTestPairs.DST_HOST_ID); assumeTrue(data.vdsmVm().getVmDynamic().getStatus() == VMStatus.MigratingTo); //then vmAnalyzer.analyze(); assertNull(vmAnalyzer.getVmDynamicToSave()); } @Theory public void prepareGuestAgentNetworkDevicesForUpdate() { // TODO add tests } @Before public void before() { for (VmTestPairs data: VmTestPairs.values()) { data.reset(); } MockitoAnnotations.initMocks(this); } public void initMocks(VmTestPairs vmData, boolean run) { stubDaos(); when(vdsManager.getVdsId()).thenReturn(VmTestPairs.SRC_HOST_ID); when(vdsManager.getClusterId()).thenReturn(VmTestPairs.CLUSTER_ID); when(vdsManager.getCopyVds()).thenReturn(vdsManagerVds); when(vmManager.isColdReboot()).thenReturn(false); when(vmManager.isAutoStart()).thenReturn(vmData.dbVm() != null ? vmData.dbVm().isAutoStartup() : false); when(vmManager.getStatistics()).thenReturn(new VmStatistics()); when(resourceManager.getVdsManager(any(Guid.class))).thenReturn(vdsManager); // -- default behaviors -- // dst host is up mockDstHostStatus(VDSStatus.Up); // -- end of behaviors -- vmAnalyzer = spy(new VmAnalyzer( vmData.dbVm() != null ? vmData.dbVm().getDynamicData() : null, vmData.vdsmVm(), false, vdsManager, auditLogDirector, resourceManager, vdsDynamicDao, null)); doNothing().when(vmAnalyzer).resetVmInterfaceStatistics(); doReturn(vmManager).when(vmAnalyzer).getVmManager(); VDSReturnValue vdsReturnValue = new VDSReturnValue(); vdsReturnValue.setSucceeded(true); doReturn(vdsReturnValue).when(vmAnalyzer).runVdsCommand(any(VDSCommandType.class), any(VDSParametersBase.class)); if (run) { vmAnalyzer.analyze(); } } private void stubDaos() { mockVdsDao(); } private void mockVdsDao() { when(vdsDynamicDao.get(VmTestPairs.SRC_HOST_ID)).thenReturn(srcHost); when(vdsDynamicDao.get(VmTestPairs.DST_HOST_ID)).thenReturn(dstHost); } private void mockDstHostStatus(VDSStatus status) { when(dstHost.getStatus()).thenReturn(status); } }