package com.emc.storageos.volumecontroller.impl.smis.job; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.StorageSystem; 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.smis.CIMConnectionFactory; import com.emc.storageos.volumecontroller.impl.smis.CIMPropertyFactory; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.wbem.CloseableIterator; import javax.wbem.WBEMException; import javax.wbem.client.WBEMClient; import java.net.URI; import java.util.ArrayList; import java.util.Calendar; import java.util.Iterator; import java.util.List; import java.util.Map; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CIM_STORAGE_VOLUME; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_DEVICE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_EMC_RG_SOURCE_INSTANCE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_EMC_RG_TARGET_INSTANCE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_SV_SOURCE_DEVICE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_SV_TARGET_DEVICE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.PS_REPLICA_PAIR_VIEW; /** * * ViPR Job created when an underlying CIM job is created to create * and link a new target volume group to a group snapshot point-in-time * copy represented in ViPR by a BlockSnapshotSession instance. * * @author Ian Bibby */ public class SmisBlockSnapshotSessionLinkTargetGroupJob extends SmisSnapShotJob { private static final Logger log = LoggerFactory.getLogger(SmisBlockSnapshotSessionLinkTargetGroupJob.class); private static final String JOB_NAME = SmisBlockSnapshotSessionLinkTargetGroupJob.class.getSimpleName(); private Map<String, URI> srcNativeIdToSnapshotMap; private String sourceGroupName; private String targetGroupName; private String snapSessionInstance; public SmisBlockSnapshotSessionLinkTargetGroupJob(CIMObjectPath cimJob, URI storageSystem, TaskCompleter taskCompleter) { super(cimJob, storageSystem, taskCompleter, JOB_NAME); } public void setSrcNativeIdToSnapshotMap(Map<String, URI> srcNativeIdToSnapshotMap) { this.srcNativeIdToSnapshotMap = srcNativeIdToSnapshotMap; } public void setSourceGroupName(String sourceGroupName) { this.sourceGroupName = sourceGroupName; } public void setTargetGroupName(String targetGroupName) { this.targetGroupName = targetGroupName; } public void setSnapSessionInstance(String snapSessionInstance) { this.snapSessionInstance = snapSessionInstance; } @Override public void updateStatus(JobContext jobContext) throws Exception { JobStatus jobStatus = getJobStatus(); try { switch(jobStatus) { case IN_PROGRESS: return; case SUCCESS: processJobSuccess(jobContext); break; case FAILED: case FATAL_ERROR: processJobFailure(jobContext); break; } } finally { super.updateStatus(jobContext); } } private void processJobSuccess(JobContext jobContext) throws Exception { DbClient dbClient = jobContext.getDbClient(); try { CIMConnectionFactory cimConnectionFactory = jobContext.getCimConnectionFactory(); WBEMClient client = getWBEMClient(dbClient, cimConnectionFactory); CIMObjectPath targetRepGrpPath = getAssociatedTargetReplicationGroupPath(client); log.info("Processing target replication group: {}", targetRepGrpPath); List<CIMObjectPath> replicaPairViews = getAssociatedReplicaPairViews(client, targetRepGrpPath); for (CIMObjectPath replicaPairViewPath : replicaPairViews) { log.info("Processing replica pair view instance: {}", replicaPairViewPath); CIMInstance replicaPairView = client.getInstance(replicaPairViewPath, false, false, PS_REPLICA_PAIR_VIEW); // Verify that ReplicaPairView references our groups String srcGrpInstance = getInstancePropertyValue(replicaPairView, CP_EMC_RG_SOURCE_INSTANCE_ID); String tgtGrpInstance = getInstancePropertyValue(replicaPairView, CP_EMC_RG_TARGET_INSTANCE_ID); // ReplicaPairView references src/tgt replication groups as <symm-id>+<group-name>, hence #contains if (!srcGrpInstance.contains(sourceGroupName) || !tgtGrpInstance.contains(targetGroupName)) { log.warn("ReplicaPairView did not match source/target groups: {}/{}", sourceGroupName, targetGroupName); continue; } String srcIdProp = (String) replicaPairView.getPropertyValue(CP_SV_SOURCE_DEVICE_ID); String tgtIdProp = (String) replicaPairView.getPropertyValue(CP_SV_TARGET_DEVICE_ID); if (srcNativeIdToSnapshotMap.containsKey(srcIdProp)) { URI blockSnapshotURI = srcNativeIdToSnapshotMap.get(srcIdProp); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, blockSnapshotURI); BlockObject sourceObj = BlockObject.fetch(dbClient, snapshot.getParent().getURI()); CIMObjectPath volumePath = getAssociatedTargetVolume(client, replicaPairViewPath, tgtIdProp); CIMInstance volume = client.getInstance(volumePath, false, false, null); String volumeElementName = CIMPropertyFactory.getPropertyValue(volume, SmisConstants.CP_ELEMENT_NAME); log.info("volumeElementName: {}", volumeElementName); String volumeWWN = CIMPropertyFactory.getPropertyValue(volume, SmisConstants.CP_WWN_NAME); log.info("volumeWWN: {}", volumeWWN); String volumeAltName = CIMPropertyFactory.getPropertyValue(volume, SmisConstants.CP_NAME); log.info("volumeAltName: {}", volumeAltName); StorageSystem system = dbClient.queryObject(StorageSystem.class, getStorageSystemURI()); snapshot.setNativeId(tgtIdProp); snapshot.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(system, snapshot)); snapshot.setDeviceLabel(volumeElementName); snapshot.setInactive(false); snapshot.setIsSyncActive(Boolean.TRUE); snapshot.setCreationTime(Calendar.getInstance()); snapshot.setWWN(volumeWWN.toUpperCase()); snapshot.setAlternateName(volumeAltName); snapshot.setSettingsInstance(snapSessionInstance); snapshot.setReplicationGroupInstance(tgtGrpInstance); commonSnapshotUpdate(snapshot, volume, client, system, sourceObj.getNativeId(), tgtIdProp, false, dbClient); log.info(String .format("For target volume path %1$s, going to set blocksnapshot %2$s nativeId to %3$s (%4$s). Associated volume is %5$s (%6$s)", volumePath.toString(), snapshot.getId().toString(), tgtIdProp, volumeElementName, sourceObj.getNativeId(), sourceObj.getDeviceLabel())); dbClient.updateObject(snapshot); } } } catch (Exception e) { setPostProcessingErrorStatus("Internal error in link snapshot session target group job status processing: " + e.getMessage()); log.error("Internal error in link snapshot session target group job status processing", e); throw e; } } private void processJobFailure(JobContext jobContext) { log.info("Failed to link target group {} to source snap session group {}", targetGroupName, sourceGroupName); DbClient dbClient = jobContext.getDbClient(); Iterator<BlockSnapshot> iterator = dbClient.queryIterativeObjects(BlockSnapshot.class, srcNativeIdToSnapshotMap.values()); ArrayList<BlockSnapshot> snapshots = Lists.newArrayList(iterator); for (BlockSnapshot snapshot : snapshots) { snapshot.setInactive(true); } dbClient.updateObject(snapshots); } private List<CIMObjectPath> getAssociatedReplicaPairViews(WBEMClient client, CIMObjectPath targetRepGrpPath) throws WBEMException { CloseableIterator<CIMObjectPath> it = null; List<CIMObjectPath> result = new ArrayList<>(); try { it = client.associatorNames(targetRepGrpPath, null, SmisConstants.SE_REPLICA_PAIR_VIEW, null, null); while (it.hasNext()) { result.add(it.next()); } return result; } finally { if (it != null) { it.close(); } } } private CIMObjectPath getAssociatedTargetReplicationGroupPath(WBEMClient client) throws WBEMException { CloseableIterator<CIMObjectPath> it = null; try { it = client.associatorNames(getCimJob(), null, SmisConstants.SE_REPLICATION_GROUP, null, null); // Only one group is expected. if (it.hasNext()) { return it.next(); } } finally { if (it != null) { it.close(); } } throw new IllegalStateException("Expected a single target replication group but found none"); } private CIMObjectPath getAssociatedTargetVolume(WBEMClient client, CIMObjectPath replicaPairView, String targetDeviceId) throws WBEMException { CloseableIterator<CIMObjectPath> it = null; try { it = client.associatorNames(replicaPairView, null, CIM_STORAGE_VOLUME, null, null); while (it.hasNext()) { CIMObjectPath volume = it.next(); String deviceId = volume.getKeyValue(CP_DEVICE_ID).toString(); if (targetDeviceId.equals(deviceId)) { return volume; } } } finally { if (it != null) { it.close(); } } throw new IllegalStateException( String.format("Expected an associated volume with nativeID %s but found none", targetDeviceId)); } private String getInstancePropertyValue(CIMInstance instance, String propertyName) { Object value = instance.getPropertyValue(propertyName); return value == null ? "" : value.toString(); } }