/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.job; import java.net.URI; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.cim.CIMObjectPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.OpStatusMap; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.StringSetMap; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.volumecontroller.JobContext; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.BlockSnapshotSessionUnlinkTargetCompleter; /** * ViPR Job created when an underlying CIM job is created to unlink * a target volume from an array snapshot. */ @SuppressWarnings("serial") public class SmisBlockSnapshotSessionUnlinkTargetJob extends SmisJob { // The unique job name. private static final String JOB_NAME = "SmisBlockSnapshotSessionUnlinkTargetJob"; // Reference to a logger. private static final Logger s_logger = LoggerFactory.getLogger(SmisBlockSnapshotSessionUnlinkTargetJob.class); /** * Constructor. * * @param cimJob The CIM object path of the underlying CIM Job. * @param systemURI The URI of the storage system. * @param taskCompleter A reference to the task completer. */ public SmisBlockSnapshotSessionUnlinkTargetJob(CIMObjectPath cimJob, URI systemURI, TaskCompleter taskCompleter) { super(cimJob, systemURI, taskCompleter, JOB_NAME); } /** * {@inheritDoc} */ @Override public void updateStatus(JobContext jobContext) throws Exception { DbClient dbClient = jobContext.getDbClient(); JobStatus jobStatus = getJobStatus(); BlockSnapshotSessionUnlinkTargetCompleter completer = (BlockSnapshotSessionUnlinkTargetCompleter) getTaskCompleter(); boolean deleteTarget = completer.getDeleteTarget(); try { if (jobStatus == JobStatus.IN_PROGRESS) { return; } if (jobStatus == JobStatus.SUCCESS) { // If we successfully unlinked the target from the array // snapshot, but we are not deleting the target, we need // to convert the BlockSnapshot instance to a Volume instance // as the device is no longer a snapshot target and so should // not be represented by a BlockSnapshot instance in ViPR. if (!deleteTarget) { List<BlockSnapshot> allSnapshots = completer.getAllSnapshots(dbClient); promoteSnapshotsToVolume(allSnapshots, dbClient); } else { // TBD - Update capacity of storage pools when deleted? // SmisUtils.updateStoragePoolCapacity(dbClient, client, poolURI); } s_logger.info("Post-processing successful for unlink snapshot session target for task ", getTaskCompleter().getOpId()); } else if (jobStatus == JobStatus.FAILED || jobStatus == JobStatus.FATAL_ERROR) { s_logger.info("Failed to unlink snapshot session target for task ", getTaskCompleter().getOpId()); } } catch (Exception e) { setPostProcessingErrorStatus("Encountered an internal error in unlink snapshot session target job status processing: " + e.getMessage()); s_logger.error("Encountered an internal error in unlink snapshot session target job status processing", e); } finally { if (!deleteTarget) { // We only want to invoke the completer if we are // not deleting the target after we unlink the target // from the array snapshot. super.updateStatus(jobContext); } } } /** * Promotes a list of BlockSnapshot instances to their Volume equivalents. For BlockSnapshot * instances that are part of a target ReplicationGroup, a BlockConsistencyGroup shall be created * as part of the promotion. * * @param snapshots List of BlockSnapshot instances. * @param dbClient Database client. */ private void promoteSnapshotsToVolume(List<BlockSnapshot> snapshots, DbClient dbClient) { Map<String, BlockConsistencyGroup> groupCache = new HashMap<>(); for (BlockSnapshot snapshot : snapshots) { // We check to make sure there is not already a volume with the // native GUID of the snapshot. This could be the case if we are // unlinking a target after restoring a source volume from a // linked target volume. In that case, a linked target was created // to represent the source volume and now we are unlinking that // linked target. The volume in this case already exists. List<Volume> volumesWithNativeId = CustomQueryUtility.getActiveVolumeByNativeGuid(dbClient, snapshot.getNativeGuid()); if (volumesWithNativeId.isEmpty()) { URI cgId = getBlockConsistencyGroupForPromotedSnapshot(snapshot, groupCache, dbClient); URI sourceObjURI = snapshot.getParent().getURI(); if (URIUtil.isType(sourceObjURI, Volume.class)) { Volume sourceVolume = dbClient.queryObject(Volume.class, sourceObjURI); // Create a new volume to represent the former snapshot target. // We get what we can from the snapshot and for what is not // available in the snapshot, we get from the source. Volume volume = new Volume(); volume.setId(URIUtil.createId(Volume.class)); volume.setCreationTime(snapshot.getCreationTime()); volume.setWWN(snapshot.getWWN()); volume.setNativeGuid(snapshot.getNativeGuid()); volume.setNativeId(snapshot.getNativeId()); volume.setLabel(snapshot.getLabel()); volume.setDeviceLabel(snapshot.getDeviceLabel()); volume.setAlternateName(snapshot.getAlternateName()); volume.setSyncActive(true); volume.setAccessState(sourceVolume.getAccessState()); volume.setCapacity(sourceVolume.getCapacity()); volume.setProvisionedCapacity(snapshot.getProvisionedCapacity()); volume.setAllocatedCapacity(snapshot.getAllocatedCapacity()); volume.setThinlyProvisioned(sourceVolume.getThinlyProvisioned()); volume.setVirtualPool(sourceVolume.getVirtualPool()); // It is understood that this is not necessary true. volume.setVirtualArray(snapshot.getVirtualArray()); volume.setProject(snapshot.getProject()); volume.setTenant(sourceVolume.getTenant()); volume.setStorageController(snapshot.getStorageController()); volume.setSystemType(snapshot.getSystemType()); volume.setPool(sourceVolume.getPool()); // It is understood that this is not necessarily true. StringSet protocols = new StringSet(); protocols.addAll(snapshot.getProtocol()); volume.setProtocol(protocols); volume.setOpStatus(new OpStatusMap()); volume.setConsistencyGroup(cgId); String repGrpInstance = snapshot.getReplicationGroupInstance(); if (!NullColumnValueGetter.isNullValue(repGrpInstance)) { volume.setReplicationGroupInstance(repGrpInstance.substring(repGrpInstance.indexOf("+") + 1)); } dbClient.createObject(volume); } } } } /** * Given a CG snapshot that is to be promoted, create a new BlockConsistencyGroup based on its * ReplicationGroup. A group cache parameter is accepted and serves to cache BlockConsistencyGroup * instances that have already been created. * * @param snapshot BlockSnapshot being promoted. * @param groupCache Cache mapping of ReplicationGroup name to BlockConsistencyGroup instances. * @param dbClient Database client. * @return BlockConsistencyGroup URI or null. */ private URI getBlockConsistencyGroupForPromotedSnapshot(BlockSnapshot snapshot, Map<String, BlockConsistencyGroup> groupCache, DbClient dbClient) { if (!snapshot.hasConsistencyGroup()) { return null; } // Create new BlockConsistencyGroup instances to track the existing target groups. String groupInstance = snapshot.getReplicationGroupInstance(); BlockConsistencyGroup cg = null; if (groupCache.containsKey(groupInstance)) { cg = groupCache.get(groupInstance); } else { cg = new BlockConsistencyGroup(); cg.setId(URIUtil.createId(BlockConsistencyGroup.class)); Project project = dbClient.queryObject(Project.class, snapshot.getProject().getURI()); cg.setProject(new NamedURI(project.getId(), project.getLabel())); cg.setTenant(new NamedURI(project.getTenantOrg().getURI(), project.getTenantOrg().getName())); String repGrpName = groupInstance.substring(groupInstance.indexOf("+") + 1, groupInstance.length()); cg.setLabel(repGrpName); StringSetMap map = new StringSetMap(); map.put(snapshot.getStorageController().toString(), new StringSet(Arrays.asList(repGrpName))); cg.setSystemConsistencyGroups(map); StringSet types = new StringSet(); types.add(BlockConsistencyGroup.Types.LOCAL.name()); cg.setTypes(types); cg.setRequestedTypes(types); cg.setStorageController(snapshot.getStorageController()); s_logger.info("Creating new BlockConsistencyGroup for ReplicationGroup {} with ID: {}", groupInstance, cg.getId()); dbClient.createObject(cg); groupCache.put(groupInstance, cg); } return cg.getId(); } }