package org.ovirt.engine.core.bll.pm;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.LinkedList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.ovirt.engine.core.bll.BaseCommandTest;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.FenceVdsActionParameters;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.VdsDynamic;
import org.ovirt.engine.core.common.businessentities.pm.FenceActionType;
import org.ovirt.engine.core.common.businessentities.pm.FenceAgent;
import org.ovirt.engine.core.common.businessentities.pm.FenceOperationResult;
import org.ovirt.engine.core.common.businessentities.pm.FenceOperationResult.Status;
import org.ovirt.engine.core.common.businessentities.pm.PowerStatus;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.interfaces.VDSBrokerFrontend;
import org.ovirt.engine.core.common.vdscommands.SetVdsStatusVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogable;
import org.ovirt.engine.core.dao.VdsDao;
import org.ovirt.engine.core.dao.VdsDynamicDao;
import org.ovirt.engine.core.dao.VmDao;
@RunWith(MockitoJUnitRunner.class)
public class StartVdsCommandTest extends BaseCommandTest {
private static final String HOST_NAME = "HostName";
private static final Guid FENCECD_HOST_ID = new Guid("11111111-1111-1111-1111-111111111111");
private static final Guid FENCECD_HOST_CLUSTER_ID = new Guid("22222222-2222-2222-2222-222222222222");
@Mock
private HostFenceActionExecutor executor;
private FenceAgent agent1;
private FenceAgent agent2;
@Mock
private VdsDao vdsDao;
@Mock
private VdsDynamicDao vdsDynamicDao;
@Mock
private VmDao vmDao;
@Mock
private VDSBrokerFrontend vdsBrokerFrontend;
@Mock
private AuditLogDirector auditLogDirector;
@Spy
@InjectMocks
private StartVdsCommand<FenceVdsActionParameters> command =
new StartVdsCommand<>(new FenceVdsActionParameters(FENCECD_HOST_ID), null);
@Before
public void setup() {
initAgents();
mockDbFacades();
initCommand();
}
private void mockDbFacades() {
mockVdsDao();
mockVdsDynamicDao();
}
private void mockVdsDao() {
VDS vds = createHost();
when(vdsDao.get(FENCECD_HOST_ID)).thenReturn(vds);
}
private void mockVdsDynamicDao() {
VdsDynamic currentVds = new VdsDynamic();
currentVds.setId(FENCECD_HOST_ID);
currentVds.setStatus(VDSStatus.Reboot);
when(vdsDynamicDao.get(FENCECD_HOST_ID)).thenReturn(currentVds);
}
private void initAgents() {
agent1 = new FenceAgent();
agent1.setOrder(1);
agent2 = new FenceAgent();
agent2.setOrder(2);
}
private void initCommand() {
doReturn(executor).when(command).createHostFenceActionExecutor(any(), any());
command.setClusterId(FENCECD_HOST_CLUSTER_ID);
}
private VDS createHost() {
VDS vds = new VDS();
vds.setId(FENCECD_HOST_ID);
vds.setClusterId(FENCECD_HOST_CLUSTER_ID);
vds.setVdsName(HOST_NAME);
vds.setStatus(VDSStatus.Up);
List<FenceAgent> agents = new LinkedList<>();
agents.add(agent1);
agents.add(agent2);
vds.setFenceAgents(agents);
return vds;
}
private void mockExecutor(boolean success) {
FenceOperationResult result;
if (success) {
result = new FenceOperationResult(Status.SUCCESS, PowerStatus.ON);
} else {
result = new FenceOperationResult(Status.ERROR, PowerStatus.UNKNOWN);
}
doReturn(result).when(executor).fence(any(FenceActionType.class));
}
/**
* This test verifies that if the fence operation fails, the initial status is restored (re-set). Without necessary
* mocking, EngineException is thrown and the command fails. In the finally() clause, SetVdsStatus is invoked to
* restore the old status. This test verifies that this invocation was made.
*/
@Test
public void onFailureResetInitialStatus() {
mockExecutor(false);
try {
command.executeCommand();
} catch (EngineException exception) {
verify(vdsBrokerFrontend).runVdsCommand(eq(VDSCommandType.SetVdsStatus),
any(SetVdsStatusVDSCommandParameters.class));
return;
}
fail();
}
/**
* This test verifies that when the fence operation is successful, the return value contains all that is should:
* succeeded=true, the agents used, and the object returned.
*/
@Test
public void onSuccessReturnValueOk() {
mockExecutor(true);
doNothing().when(command).teardown();
command.executeCommand();
assertTrue(command.getReturnValue().getSucceeded());
Object commandReturnValue = command.getReturnValue().getActionReturnValue();
assertNotNull(commandReturnValue);
assertTrue(commandReturnValue instanceof FenceOperationResult);
FenceOperationResult commandReturnValueCasted = (FenceOperationResult) commandReturnValue;
assertEquals(Status.SUCCESS, commandReturnValueCasted.getStatus());
}
/**
* This test verifies that auditing is done. Due to a difficulty to set an expectation for a specific AuditLog
* object (equals() compares too many fields, it's complicated to accurately set the expectation), the test counts
* the times that an audit message is saved to the database. In case of success, there are 2 audit messages, one
* upon entering the command, and the other upon completion, to report success.
*/
@Test
public void onSuccessAudit() {
mockExecutor(true);
doNothing().when(command).teardown();
command.executeCommand();
verify(auditLogDirector, times(2)).log(any(AuditLogable.class), any(AuditLogType.class));
}
/**
* This test verifies that when the fence operation fails, not due to a fencing-policy restriction, an alert is
* shown. Due to a difficulty to set an expectation for a specific AuditLog object (equals() compares too many
* fields, it's complicated to accurately set the expectation), the test counts the times that an audit message is
* saved to the database. In case of success, there are 2 audit messages. In case of failure the second audit
* message isn't reached, but because of the auditing of the alert, there *are still* 2 audit messages.
*/
@Test
public void onFailureAlertShown() {
mockExecutor(false);
try {
command.executeCommand();
fail();
} catch (EngineException ex) {
verify(auditLogDirector, times(3)).log(any(AuditLogable.class), any(AuditLogType.class));
}
}
}