/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.service.impl.placement.VpoolUse;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.BlockConsistencyGroup;
import com.emc.storageos.db.client.model.Project;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.VirtualArray;
import com.emc.storageos.db.client.model.VirtualPool;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.model.TaskList;
import com.emc.storageos.model.TaskResourceRep;
import com.emc.storageos.model.block.VolumeCreate;
import com.emc.storageos.svcs.errorhandling.model.ServiceCoded;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException;
import com.emc.storageos.volumecontroller.Recommendation;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
/**
* Background thread that runs the placement, scheduling, and controller dispatching of a create volume
* request. This allows the API to return a Task object quickly.
*/
class CreateVolumeSchedulingThread implements Runnable {
static final Logger _log = LoggerFactory.getLogger(CreateVolumeSchedulingThread.class);
private final BlockService blockService;
private VirtualArray varray;
private Project project;
private VirtualPool vpool;
private VirtualPoolCapabilityValuesWrapper capabilities;
private TaskList taskList;
private String task;
private BlockConsistencyGroup consistencyGroup;
private ArrayList<String> requestedTypes;
private VolumeCreate param;
private BlockServiceApi blockServiceImpl;
public CreateVolumeSchedulingThread(BlockService blockService, VirtualArray varray, Project project,
VirtualPool vpool,
VirtualPoolCapabilityValuesWrapper capabilities,
TaskList taskList, String task, BlockConsistencyGroup consistencyGroup, ArrayList<String> requestedTypes,
VolumeCreate param,
BlockServiceApi blockServiceImpl) {
this.blockService = blockService;
this.varray = varray;
this.project = project;
this.vpool = vpool;
this.capabilities = capabilities;
this.taskList = taskList;
this.task = task;
this.consistencyGroup = consistencyGroup;
this.requestedTypes = requestedTypes;
this.param = param;
this.blockServiceImpl = blockServiceImpl;
}
@Override
public void run() {
_log.info("Starting scheduling/placement thread...");
// Call out placementManager to get the recommendation for placement.
try {
Map<VpoolUse, List<Recommendation>> recommendationMap =
this.blockService._placementManager.getRecommendationsForVirtualPool(
varray, project, vpool, capabilities);
if (recommendationMap.isEmpty()) {
throw APIException.badRequests.
noMatchingStoragePoolsForVpoolAndVarray(vpool.getLabel(), varray.getLabel());
}
// At this point we are committed to initiating the request.
if (consistencyGroup != null) {
consistencyGroup.addRequestedTypes(requestedTypes);
this.blockService._dbClient.updateObject(consistencyGroup);
}
// Call out to the respective block service implementation to prepare
// and create the volumes based on the recommendations.
blockServiceImpl.createVolumes(param, project, varray, vpool, recommendationMap, taskList, task, capabilities);
} catch (Exception ex) {
for (TaskResourceRep taskObj : taskList.getTaskList()) {
if (ex instanceof ServiceCoded) {
this.blockService._dbClient.error(Volume.class, taskObj.getResource().getId(), taskObj.getOpId(), (ServiceCoded) ex);
} else {
this.blockService._dbClient.error(Volume.class, taskObj.getResource().getId(), taskObj.getOpId(),
InternalServerErrorException.internalServerErrors
.unexpectedErrorVolumePlacement(ex));
}
_log.error(ex.getMessage(), ex);
taskObj.setMessage(ex.getMessage());
// Set the volumes to inactive
Volume volume = this.blockService._dbClient.queryObject(Volume.class, taskObj.getResource().getId());
volume.setInactive(true);
// clean up the consistency group
if (consistencyGroup != null) {
StringSet cgRequestTypes = consistencyGroup.getRequestedTypes();
cgRequestTypes.removeAll(cgRequestTypes);
this.blockService._dbClient.updateObject(consistencyGroup);
}
this.blockService._dbClient.updateObject(volume);
}
}
_log.info("Ending scheduling/placement thread...");
}
/**
* Static method to execute the API task in the background to create an export group.
*
* @param blockService block service ("this" from caller)
* @param executorService executor service that manages the thread pool
* @param dbClient db client
* @param varray virtual array
* @param project project
* @param vpool virtual pool
* @param capabilities capabilities object
* @param taskList list of tasks
* @param task task ID
* @param consistencyGroup consistency group
* @param requestedTypes requested types
* @param param volume creation request params
* @param blockServiceImpl block service impl to call
*/
public static void executeApiTask(BlockService blockService, ExecutorService executorService, DbClient dbClient, VirtualArray varray,
Project project,
VirtualPool vpool, VirtualPoolCapabilityValuesWrapper capabilities,
TaskList taskList, String task, BlockConsistencyGroup consistencyGroup, ArrayList<String> requestedTypes,
VolumeCreate param,
BlockServiceApi blockServiceImpl) {
CreateVolumeSchedulingThread schedulingThread = new CreateVolumeSchedulingThread(blockService, varray,
project, vpool,
capabilities, taskList, task, consistencyGroup, requestedTypes, param, blockServiceImpl);
try {
executorService.execute(schedulingThread);
} catch (Exception e) {
for (TaskResourceRep taskObj : taskList.getTaskList()) {
String message = "Failed to execute volume creation API task for resource " + taskObj.getResource().getId();
_log.error(message);
taskObj.setMessage(message);
// Set the volumes to inactive
Volume volume = dbClient.queryObject(Volume.class, taskObj.getResource().getId());
volume.setInactive(true);
dbClient.updateObject(volume);
}
}
}
}