/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.cinder.job; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.cinder.CinderConstants; import com.emc.storageos.cinder.CinderEndPointInfo; import com.emc.storageos.cinder.api.CinderApi; import com.emc.storageos.cinder.model.VolumeShowResponse; import com.emc.storageos.db.client.DbClient; 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.volumecontroller.JobContext; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator; import com.emc.storageos.volumecontroller.impl.cinder.CinderUtils; /** * Abstract Implementation for cinder volume creation status * check and update the corresponding objects in the Database. * */ public abstract class AbstractCinderVolumeCreateJob extends CinderJob { private static final long serialVersionUID = -5488768452792486576L; private static final Logger logger = LoggerFactory.getLogger(AbstractCinderVolumeCreateJob.class); private URI storagePoolUri = null; private Map<String, URI> volumeIds = null; /** * @param jobId * @param jobName * @param storageSystem * @param componentType * @param ep * @param taskCompleter */ public AbstractCinderVolumeCreateJob(String jobId, String jobName, URI storageSystem, String componentType, CinderEndPointInfo ep, TaskCompleter taskCompleter, URI storagePoolUri, Map<String, URI> volumeIds) { super(jobId, jobName, storageSystem, componentType, ep, taskCompleter); this.storagePoolUri = storagePoolUri; this.volumeIds = volumeIds; } /** * Called to update the job status when the volume create job completes. * This is common update code for volume create operations. * * @param jobContext The job context. */ @Override public void updateStatus(JobContext jobContext) throws Exception { DbClient dbClient = jobContext.getDbClient(); try { // Do nothing if the job is not completed yet if (status == JobStatus.IN_PROGRESS) { return; } 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()); CinderApi cinderApi = jobContext.getCinderApiFactory().getApi(storageSystem.getActiveProviderURI(), getEndPointInfo()); // If terminal state update storage pool capacity and remove reservation for volumes capacity // from pool's reserved capacity map. StoragePool storagePool = null; if (status == JobStatus.SUCCESS || status == JobStatus.FAILED) { storagePool = dbClient.queryObject(StoragePool.class, storagePoolUri); StringMap reservationMap = storagePool.getReservedCapacityMap(); for (URI volumeId : getTaskCompleter().getIds()) { // remove from reservation map reservationMap.remove(volumeId.toString()); } dbClient.persistObject(storagePool); } if (status == JobStatus.SUCCESS) { List<URI> volumes = new ArrayList<URI>(); Calendar now = Calendar.getInstance(); URI volumeId = getTaskCompleter().getId(); volumes.add(volumeId); for (Map.Entry<String, URI> entry : volumeIds.entrySet()) { VolumeShowResponse volumeDetails = cinderApi.showVolume(entry.getKey()); processVolume(entry.getValue(), volumeDetails, dbClient, now, logMsgBuilder); // Adjust the storage pool's capacity CinderUtils.updateStoragePoolCapacity(dbClient, cinderApi, storagePool, volumeDetails.volume.size, false); } } 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())); Volume volume = dbClient.queryObject(Volume.class, id); volume.setInactive(true); dbClient.persistObject(volume); } } logger.info(logMsgBuilder.toString()); } catch (Exception e) { logger.error("Caught an exception while trying to updateStatus for CinderCreateVolumeJob", e); setErrorStatus("Encountered an internal error during volume create job status processing : " + e.getMessage()); } finally { super.updateStatus(jobContext); } } @Override protected boolean isJobSucceeded(String currentStatus) { return (CinderConstants.ComponentStatus.AVAILABLE.getStatus().equalsIgnoreCase(currentStatus)); } @Override protected boolean isJobFailed(String currentStatus) { return (CinderConstants.ComponentStatus.ERROR.getStatus().equalsIgnoreCase(currentStatus)); } /** * Process the Volume details received from cinder by setting the * volume attributes in Volume DB object. * * @param volume * @param volDetails * @param now */ private void processVolume(URI volumeId, VolumeShowResponse volDetails, DbClient dbClient, Calendar now, StringBuilder logMsgBuilder) { try { Volume volume = dbClient.queryObject(Volume.class, volumeId); volume.setCreationTime(now); volume.setNativeId(volDetails.volume.id); volume.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(dbClient, volume)); long capacityInBytes = Long.valueOf(volDetails.volume.size) * 1024L * 1024L * 1024L; volume.setAllocatedCapacity(capacityInBytes); // @TODO currently we don't get wwn for volumes. Setting the id generated as wwn volume.setWWN(volDetails.volume.id); volume.setProvisionedCapacity(capacityInBytes); volume.setInactive(false); dbClient.persistObject(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) { logger.error("Caught an exception while trying to update volume attributes", e); } } }