/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.hds.prov.job;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import org.milyn.payload.JavaResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.StoragePool;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringMap;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.hds.HDSConstants;
import com.emc.storageos.hds.api.HDSApiClient;
import com.emc.storageos.hds.model.LDEV;
import com.emc.storageos.hds.model.LogicalUnit;
import com.emc.storageos.hds.model.ObjectLabel;
import com.emc.storageos.hds.model.Pool;
import com.emc.storageos.volumecontroller.JobContext;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator;
import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSUtils;
/**
* This class will have a base implementation of the
* updateStatus for create volume operations.
*/
public abstract class HDSAbstractCreateVolumeJob extends HDSJob {
private static final Logger _log = LoggerFactory.getLogger(HDSAbstractCreateVolumeJob.class);
private URI storagePoolURI;
public HDSAbstractCreateVolumeJob(String hdsJob, URI storageSystem, URI storagePoolURI, TaskCompleter taskCompleter, String name) {
super(hdsJob, storageSystem, taskCompleter, name);
this.storagePoolURI = storagePoolURI;
}
/**
* Called to update the job status when the volume create job completes.
* <p/>
* This is common update code for volume create operations.
*
* @param jobContext The job context.
*/
@Override
public void updateStatus(JobContext jobContext) throws Exception {
List<LogicalUnit> luList = null;
DbClient dbClient = jobContext.getDbClient();
try {
if (_status == JobStatus.IN_PROGRESS) {
return;
}
int volumeCount = 0;
String opId = getTaskCompleter().getOpId();
StringBuilder logMsgBuilder = new StringBuilder(String.format("Updating status of job %s to %s", opId, _status.name()));
StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class, getStorageSystemURI());
HDSApiClient hdsApiClient = jobContext.getHdsApiFactory().getClient(HDSUtils.getHDSServerManagementServerInfo(storageSystem),
storageSystem.getSmisUserName(), storageSystem.getSmisPassword());
// If terminal state update storage pool capacity and remove reservation for volumes capacity
// from pool's reserved capacity map.
if (_status == JobStatus.SUCCESS || _status == JobStatus.FAILED) {
StoragePool storagePool = dbClient.queryObject(StoragePool.class, storagePoolURI);
HDSUtils.updateStoragePoolCapacity(dbClient, hdsApiClient, storagePool);
StringMap reservationMap = storagePool.getReservedCapacityMap();
for (URI volumeId : getTaskCompleter().getIds()) {
// remove from reservation map
reservationMap.remove(volumeId.toString());
}
dbClient.persistObject(storagePool);
}
boolean isThinVolumeRequest = checkThinVolumesRequest(getTaskCompleter().getIds(), dbClient);
if (_status == JobStatus.SUCCESS) {
List<URI> volumes = new ArrayList<URI>();
luList = getLuListBasedOnModel(storageSystem, _javaResult, isThinVolumeRequest);
Iterator<LogicalUnit> luListItr = luList.iterator();
Calendar now = Calendar.getInstance();
while (luListItr.hasNext()) {
LogicalUnit logicalUnit = luListItr.next();
URI volumeId = getTaskCompleter().getId(volumeCount++);
volumes.add(volumeId);
processVolume(volumeId, logicalUnit, dbClient, hdsApiClient, now, logMsgBuilder);
}
} else if (_status == JobStatus.FAILED) {
for (URI id : getTaskCompleter().getIds()) {
logMsgBuilder.append("\n");
logMsgBuilder.append(String.format(
"Task %s failed to create volume: %s", opId, id.toString()));
BlockObject object = BlockObject.fetch(dbClient, id);
if (object != null) {
object.setInactive(true);
dbClient.persistObject(object);
}
}
}
_log.info(logMsgBuilder.toString());
} catch (Exception e) {
_log.error("Caught an exception while trying to updateStatus for HDSCreateVolumeJob", e);
setErrorStatus("Encountered an internal error during volume create job status processing : " + e.getMessage());
} finally {
super.updateStatus(jobContext);
}
}
/**
* This method is abstract and should be implemented by the derived class for
* specific updates or processing for a derived class.
*
* @param dbClient [in] - Client for reading/writing from/to database.
* @param client [in] - HDSApiClient for accessing Hitachi provider data
* @param volume [in] - Reference to Bourne's Volume object
*
*/
abstract void specificProcessing(DbClient dbClient, HDSApiClient client, Volume volume);
/**
* Return the LUList based on the model.
*
* @param storageSystem
* @param javaResult
* @param isThinVolumeRequest
* @return
*/
private List<LogicalUnit> getLuListBasedOnModel(StorageSystem storageSystem, JavaResult javaResult,
boolean isThinVolumeRequest) {
List<Pool> arrayGroupList = (List<Pool>) javaResult
.getBean(HDSConstants.ARRAYGROUP_RESPONSE_BEAN_ID);
List<LogicalUnit> luList = getAllLogicalUnits(arrayGroupList);
if (null == luList || luList.isEmpty()) {
luList = (List<LogicalUnit>) javaResult.getBean(HDSConstants.LOGICALUNIT_LIST_BEAN_NAME);
}
return luList;
}
/**
* When multiple thin volumes are created, volumes are reported under ArrayGroup seperately.
* Hence we should iterate through all ArrayGroups and get all the logical units.
*
* @param arrayGroupList
* @return
*/
private List<LogicalUnit> getAllLogicalUnits(List<Pool> arrayGroupList) {
List<LogicalUnit> luList = new ArrayList<LogicalUnit>();
if (null != arrayGroupList && !arrayGroupList.isEmpty()) {
for (Pool pool : arrayGroupList) {
if (null != pool.getVirtualLuList() && !pool.getVirtualLuList().isEmpty()) {
luList.addAll(pool.getVirtualLuList());
}
}
}
return luList;
}
/**
* Verifies whether the request contains thin volumes or not.
*
* @param volumeIds
* @param dbClient
* @return
*/
private boolean checkThinVolumesRequest(List<URI> volumeIds, DbClient dbClient) {
boolean isThinVolumeRequest = false;
if (null != volumeIds && !volumeIds.isEmpty()) {
for (URI volumeId : volumeIds) {
// Volume volume = dbClient.queryObject(Volume.class, volumeId);
Volume volume = (Volume) BlockObject.fetch(dbClient, volumeId);
if (volume.getThinlyProvisioned().booleanValue()) {
isThinVolumeRequest = true;
break;
}
}
}
return isThinVolumeRequest;
}
/**
* Process the LogicalUnit response received from server by setting the
* LogicalUnit attributes in Volume db object.
*
* @param volumeId : volume URI.
* @param logicalUnit : LogicalUnit.
* @param dbClient : dbClient reference.
* @param hdsApiClient : HDS APIClient reference.
* @param now : current timeStamp.
* @param logMsgBuilder: log Msg.
*/
private void processVolume(URI volumeId, LogicalUnit logicalUnit, DbClient dbClient,
HDSApiClient hdsApiClient, Calendar now, StringBuilder logMsgBuilder) {
try {
// Volume volume = dbClient.queryObject(Volume.class, volumeId);
Volume volume = (Volume) BlockObject.fetch(dbClient, volumeId);
volume.setCreationTime(now);
volume.setNativeId(String.valueOf(logicalUnit.getDevNum()));
volume.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(dbClient, volume));
long capacityInBytes = Long.valueOf(logicalUnit.getCapacityInKB()) * 1024L;
volume.setAllocatedCapacity(capacityInBytes);
volume.setWWN(HDSUtils.generateHitachiWWN(logicalUnit.getObjectID(), String.valueOf(logicalUnit.getDevNum())));
volume.setProvisionedCapacity(capacityInBytes);
volume.setInactive(false);
dbClient.persistObject(volume);
specificProcessing(dbClient, hdsApiClient, volume);
if (logMsgBuilder.length() != 0) {
logMsgBuilder.append("\n");
}
logMsgBuilder.append(String.format(
"Created volume successfully .. NativeId: %s, URI: %s", volume.getNativeId(),
getTaskCompleter().getId()));
} catch (IOException e) {
_log.error("Caught an exception while trying to update volume attributes", e);
}
}
/**
* Method will modify the name of a given volume to a generate name.
*
* @param dbClient [in] - Client instance for reading/writing from/to DB
* @param client [in] - HDSApiClient used for reading/writing from/to HiCommand DM.
* @param volume [in] - Volume object
*/
protected void changeVolumeName(DbClient dbClient, HDSApiClient client, Volume volume, String name) {
try {
_log.info(String.format("Attempting to add volume label %s to %s", name, volume.getWWN()));
StorageSystem system = dbClient.queryObject(StorageSystem.class, volume.getStorageController());
String systemObjectId = HDSUtils.getSystemObjectID(system);
LogicalUnit logicalUnit = client.getLogicalUnitInfo(systemObjectId,
HDSUtils.getLogicalUnitObjectId(volume.getNativeId(), system));
if (null != logicalUnit && null != logicalUnit.getLdevList() && !logicalUnit.getLdevList().isEmpty()) {
Iterator<LDEV> ldevItr = logicalUnit.getLdevList().iterator();
if (ldevItr.hasNext()) {
LDEV ldev = ldevItr.next();
ObjectLabel objectLabel = client.addVolumeLabel(ldev.getObjectID(), name);
volume.setDeviceLabel(objectLabel.getLabel());
dbClient.persistObject(volume);
}
} else {
_log.info("No LDEV's found on volume: {}", volume.getWWN());
}
_log.info(String.format("Volume label has been added to volume %s", volume.getWWN()));
} catch (DatabaseException e) {
_log.error("Encountered an error while trying to set the volume name", e);
} catch (Exception e) {
_log.error("Encountered an error while trying to set the volume name", e);
}
}
}