/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.sa.engine;
import java.net.URI;
import java.util.Collections;
import java.util.Iterator;
import java.util.Collection;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.emc.sa.engine.bind.Param;
import com.emc.sa.engine.service.AbstractExecutionService;
import com.emc.sa.engine.service.ExecutionService;
import com.emc.sa.engine.service.ExecutionServiceFactory;
import com.emc.sa.engine.service.ServiceNotFoundException;
import com.emc.storageos.db.client.model.uimodels.CatalogService;
import com.emc.storageos.db.client.model.uimodels.ExecutionState;
import com.emc.storageos.db.client.model.uimodels.Order;
import com.emc.storageos.db.client.model.uimodels.OrderParameter;
import com.emc.storageos.db.client.model.uimodels.OrderStatus;
import com.emc.sa.model.dao.ModelClient;
import com.emc.sa.model.mock.InMemoryDbClient;
import com.emc.sa.model.mock.StubCoordinatorClientImpl;
import com.emc.storageos.api.service.utils.DummyDBClient;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.common.VdcUtil;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.google.common.collect.Maps;
@SuppressWarnings("unused")
public class ExecutionEngineImplTest {
private static final Logger LOG = Logger.getLogger(ExecutionEngineImplTest.class);
private ModelClient modelClient;
private CoordinatorClient coordinatorClient;
@Before
public void setUp() {
VdcUtil.setDbClient(MockDbClient.create());
modelClient = new ModelClient(new InMemoryDbClient());
coordinatorClient = new StubCoordinatorClientImpl(URI.create("urn:StubCoordinatorClientImpl"));
}
@After
public void tearDown() throws Exception {
}
@Test
public void testSimpleService() {
EmptyService service = new EmptyService() {
@Param
protected String value;
@Param
protected Long size;
};
Order valid = executeOrder(service, "Simple", "value=Test", "size=10");
Assert.assertEquals(OrderStatus.SUCCESS.name(), valid.getOrderStatus());
Order invalid = executeOrder(service, "Simple", "value=Test");
Assert.assertEquals(OrderStatus.ERROR.name(), invalid.getOrderStatus());
}
@Test
public void testRollback() {
final EmptyTask firstRollbackTask = new EmptyTask("First Rollback");
final EmptyTask secondRollbackTask = new EmptyTask("Second Rollback");
ExecutionService rollbackService = new EmptyService() {
@Param
protected boolean rollback;
@Override
public void execute() throws Exception {
addRollback(firstRollbackTask);
addRollback(secondRollbackTask);
if (rollback) {
throw new Exception("Trigger rollback");
}
}
};
Order noRollbackOrder = executeOrder(rollbackService, "NoRollback", "rollback=false");
Assert.assertEquals(OrderStatus.SUCCESS.name(), noRollbackOrder.getOrderStatus());
Assert.assertFalse(firstRollbackTask.wasRun);
Assert.assertFalse(secondRollbackTask.wasRun);
Order rollbackOrder = executeOrder(rollbackService, "Rollback", "rollback=true");
Assert.assertEquals(OrderStatus.ERROR.name(), rollbackOrder.getOrderStatus());
Assert.assertTrue(firstRollbackTask.wasRun);
Assert.assertTrue(secondRollbackTask.wasRun);
}
@Test
public void testFailDuringRollback() {
final FailureTask rollbackTask = new FailureTask("Fail During Rollback", "Fail during rollback");
final EmptyTask skippedTask = new EmptyTask("Skipped Rollback");
ExecutionService rollbackService = new EmptyService() {
@Override
public void execute() throws Exception {
addRollback(skippedTask);
addRollback(rollbackTask);
throw new Exception("Trigger rollback");
}
};
Order order = executeOrder(rollbackService, "FailDuringRollback");
Assert.assertEquals(OrderStatus.ERROR.name(), order.getOrderStatus());
Assert.assertEquals(true, rollbackTask.wasRun);
Assert.assertEquals(false, skippedTask.wasRun);
}
@Test
public void testClearRollback() {
final EmptyTask rollback1 = new EmptyTask("Rollback 1");
final EmptyTask rollback2 = new EmptyTask("Rollback 2");
ExecutionService rollbackService = new EmptyService() {
@Override
public void execute() throws Exception {
addRollback(rollback1);
addRollback(rollback2);
clearRollback();
throw new Exception("Trigger rollback");
}
};
Order order = executeOrder(rollbackService, "ClearRollback");
Assert.assertEquals(OrderStatus.ERROR.name(), order.getOrderStatus());
Assert.assertEquals(false, rollback1.wasRun);
Assert.assertEquals(false, rollback2.wasRun);
}
@Test
public void testClearSomeRollback() {
final EmptyTask rollback1 = new EmptyTask("Rollback 1");
final EmptyTask rollback2 = new EmptyTask("Rollback 2");
ExecutionService rollbackService = new EmptyService() {
@Override
public void execute() throws Exception {
addRollback(rollback1);
clearRollback();
addRollback(rollback2);
throw new Exception("Trigger rollback");
}
};
Order order = executeOrder(rollbackService, "ClearSomeRollback");
Assert.assertEquals(OrderStatus.ERROR.name(), order.getOrderStatus());
Assert.assertEquals(false, rollback1.wasRun);
Assert.assertEquals(true, rollback2.wasRun);
}
@Test
public void testRemoveRollbackByType() {
final FailureTask rollback1 = new FailureTask("Rollback 1", "Fail 1");
final EmptyTask rollback2 = new EmptyTask("Rollback 2");
ExecutionService rollbackService = new EmptyService() {
@Override
public void execute() throws Exception {
addRollback(rollback1);
addRollback(rollback2);
removeRollback(FailureTask.class);
throw new Exception("Trigger rollback");
}
};
Order order = executeOrder(rollbackService, "RemoveRollbackByType");
Assert.assertEquals(OrderStatus.ERROR.name(), order.getOrderStatus());
Assert.assertEquals(false, rollback1.wasRun);
Assert.assertEquals(true, rollback2.wasRun);
}
@Test
public void testErrorCreatingService() {
ExecutionEngineImpl engine = new ExecutionEngineImpl();
engine.setModelClient(modelClient);
engine.setServiceFactory(new ExecutionServiceFactory() {
@Override
public ExecutionService createService(Order order, CatalogService catalogService)
throws ServiceNotFoundException {
throw new RuntimeException("Unexpected error");
}
});
Order order = executeOrder(engine, createOrder("ErrorCreatingService"));
Assert.assertEquals(OrderStatus.ERROR.name(), order.getOrderStatus());
}
protected Order executeOrder(ExecutionService service, String orderName, String... params) {
return executeOrder(service, createOrder(orderName, params));
}
protected Order executeOrder(ExecutionService service, Order order) {
ExecutionEngine engine = createEngine(service);
return executeOrder(engine, order);
}
protected Order executeOrder(ExecutionEngine engine, Order order) {
engine.executeOrder(order);
return order;
}
protected ExecutionEngine createEngine(ExecutionService service) {
ExecutionEngineImpl engine = new ExecutionEngineImpl();
engine.setModelClient(modelClient);
engine.setServiceFactory(new SimpleServiceFactory(service));
engine.setCoordinatorClient(coordinatorClient);
return engine;
}
protected Order createOrder(String name, String... params) {
Map<String, String> values = Maps.newLinkedHashMap();
for (String param : params) {
String paramName = StringUtils.substringBefore(param, "=");
String paramValue = StringUtils.substringAfter(param, "=");
values.put(paramName, paramValue);
}
return createOrder(name, values);
}
protected Order createOrder(String name, Map<String, String> params) {
ExecutionState state = new ExecutionState();
modelClient.save(state);
Order order = new Order();
order.setExecutionStateId(state.getId());
CatalogService service = new CatalogService();
service.setLabel(name);
modelClient.save(service);
order.setCatalogServiceId(service.getId());
modelClient.save(order);
int index = 0;
for (Map.Entry<String, String> entry : params.entrySet()) {
OrderParameter param = new OrderParameter();
param.setLabel(entry.getKey());
param.setValue(entry.getValue());
param.setOrderId(order.getId());
modelClient.save(param);
}
return order;
}
protected static class SimpleServiceFactory implements ExecutionServiceFactory {
private ExecutionService serviceInstance;
public SimpleServiceFactory(ExecutionService serviceInstance) {
this.serviceInstance = serviceInstance;
}
@Override
public ExecutionService createService(Order order, CatalogService catalogService)
throws ServiceNotFoundException {
return serviceInstance;
}
}
protected static class EmptyService extends AbstractExecutionService {
@Override
public void precheck() throws Exception {
debug("precheck parameters: %s", ExecutionUtils.currentContext().getParameters());
}
@Override
public void execute() throws Exception {
debug("execute parameters: %s", ExecutionUtils.currentContext().getParameters());
}
}
protected static class EmptyTask extends ExecutionTask<Void> {
protected boolean wasRun;
public EmptyTask(String name) {
setName(name);
}
@Override
public Void executeTask() throws Exception {
wasRun = true;
return null;
}
}
protected static class FailureTask extends EmptyTask {
protected Exception failure;
public FailureTask(String name, String message) {
this(name, new Exception(message));
}
public FailureTask(String name, Exception failure) {
super(name);
this.failure = failure;
}
@Override
public Void executeTask() throws Exception {
super.executeTask();
throw failure;
}
}
/**
* This overrides the queryIterativeObjects methods of DummyDBClient so that the URIUtil.createId will not fail. It
* queries for VirtualDataCenters, if a null iterator is returned a NPE is thrown.
*/
public static class MockDbClient extends DummyDBClient {
@Override
public <T extends DataObject> Iterator<T> queryIterativeObjects(Class<T> clazz, Collection<URI> ids) {
return queryIterativeObjects(clazz, ids, true);
}
@SuppressWarnings("unchecked")
@Override
public <T extends DataObject> Iterator<T> queryIterativeObjects(Class<T> clazz, Collection<URI> ids,
boolean activeOnly) throws DatabaseException {
return Collections.EMPTY_LIST.iterator();
}
public static MockDbClient create() {
MockDbClient client = new MockDbClient();
client.start();
return client;
}
}
}