/* * Copyright (c) 2008-2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.processor.detailedDiscovery; import java.util.Map; import java.util.Set; import javax.cim.CIMArgument; import javax.cim.CIMDataType; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.cim.CIMProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.cimadapter.connections.cim.CimObjectPathCreator; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.plugins.BaseCollectionException; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.plugins.common.domainmodel.Operation; import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.processor.StorageProcessor; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; /** * Processor to handle StorageSynchronized instances of local replica */ public class ReplicationRelationshipProcessor extends StorageProcessor { private final static Logger _logger = LoggerFactory .getLogger(ReplicationRelationshipProcessor.class); private final static String COPY_STATE = "CopyState"; private final static String COPY_METHODOLOGY = "CopyMethodology"; private final static String SYNC_TYPE = "SyncType"; private final static String SYNC_STATE = "SyncState"; private final static String EMC_RELATIONSHIP_NAME = "EMCRelationshipName"; private final static String EMC_COPY_STATE_DESC = "EMCCopyStateDesc"; private final static String INACTIVE = "INACTIVE"; private static final String COPY_STATE_SYNCHRONIZED = "4"; private static final String COPY_STATE_FRACTURED = "6"; private static final String COPY_STATE_SPLIT = "7"; private static final String COPY_STATE_INACTIVE = "8"; // replica state for clone of snapshot, which is not restorable in ViPR private static final String SNAPSHOT_CLONE_REPLICA_STATE = Volume.ReplicationState.UNKNOWN.name(); private static final String SNAPVX_COPY_METHODOLOGY = "3"; private Map<String, LocalReplicaObject> _volumeToLocalReplicaMap; private Map<String, Map<String, String>> _syncAspectMap; private Map<String, Set<String>> _duplicateSyncAspectElementNameMap; @SuppressWarnings("unchecked") @Override public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap) throws BaseCollectionException { _logger.debug("Calling ReplicationRelationshipProcessor"); _volumeToLocalReplicaMap = (Map<String, LocalReplicaObject>) keyMap .get(Constants.UN_VOLUME_LOCAL_REPLICA_MAP); _syncAspectMap = (Map<String, Map<String, String>>) keyMap .get(Constants.SNAPSHOT_NAMES_SYNCHRONIZATION_ASPECT_MAP); _duplicateSyncAspectElementNameMap = (Map<String, Set<String>>) keyMap .get(Constants.DUPLICATE_SYNC_ASPECT_ELEMENT_NAME_MAP); CIMInstance[] instances = (CIMInstance[]) getFromOutputArgs((CIMArgument[]) resultObj, SmisConstants.SYNCHRONIZATIONS); if (instances == null) { _logger.info("No {} returned", SmisConstants.SYNCHRONIZATIONS); return; } _logger.info("Total StorageSynchronized instances {}", instances.length); processInstances(instances); } private void processInstances(CIMInstance[] instances) { for (CIMInstance instance : instances) { try { CIMObjectPath targetPath = (CIMObjectPath) instance.getPropertyValue(Constants._SyncedElement); CIMObjectPath sourcePath = (CIMObjectPath) instance.getPropertyValue(Constants._SystemElement); String nativeGuid = getUnManagedVolumeNativeGuidFromVolumePath(targetPath); String srcNativeGuid = getUnManagedVolumeNativeGuidFromVolumePath(sourcePath); _logger.info("Target Native Guid {}, Source Native Guid {}", nativeGuid, srcNativeGuid); String syncType = getCIMPropertyValue(instance, SYNC_TYPE); String copyMethod = getCIMPropertyValue(instance, COPY_METHODOLOGY); LocalReplicaObject replicaObj = _volumeToLocalReplicaMap .get(nativeGuid); if (replicaObj == null) { replicaObj = new LocalReplicaObject(nativeGuid); _volumeToLocalReplicaMap.put(nativeGuid, replicaObj); } else { // Target is already in the map, must also be a source // as part of other StorageSynchronized instance(s). // Need to set source _logger.info("Found Target Local Replica Object {}", replicaObj); if (isReplicationSnapshot(syncType, copyMethod)) { StringSet fullCopies = replicaObj.getFullCopies(); if (fullCopies != null && !fullCopies.isEmpty()) { for (String fullCopyNativeGuid : fullCopies) { LocalReplicaObject fullCopy = _volumeToLocalReplicaMap.get(fullCopyNativeGuid); fullCopy.setReplicaState(SNAPSHOT_CLONE_REPLICA_STATE); } } } } // set source replicaObj.setSourceNativeGuid(srcNativeGuid); boolean isReplicaOfSnapshot = false; LocalReplicaObject srcReplicaObj = _volumeToLocalReplicaMap .get(srcNativeGuid); if (srcReplicaObj == null) { srcReplicaObj = new LocalReplicaObject(srcNativeGuid); _volumeToLocalReplicaMap.put(srcNativeGuid, srcReplicaObj); } else { // A volume could be both a source or target in corner cases // Source is already in the map, could also be a target // as part of other StorageSynchronized instance(s). // Need to set fullCopies/mirrors/snapshots accordingly _logger.info("Found Source Local Replica Object {}", srcReplicaObj); if (LocalReplicaObject.Types.BlockSnapshot.equals(srcReplicaObj.getType())) { isReplicaOfSnapshot = true; } } String systemName = targetPath.getKey(Constants._SystemName).getValue().toString(); String syncState = getCIMPropertyValue(instance, SYNC_STATE); String copyState = getCIMPropertyValue(instance, COPY_STATE); boolean inSync = COPY_STATE_SYNCHRONIZED.equals(copyState); if (isReplicationSnapshot(syncType, copyMethod)) { replicaObj.setType(LocalReplicaObject.Types.BlockSnapshot); String emcCopyState = getCIMPropertyValue(instance, EMC_COPY_STATE_DESC); if (INACTIVE.equals(emcCopyState)) { // for an inactive snapshot, needsCopyToTarget has to be set, // so that ViPR will try to "activate" it by calling copySnapshotToTarget during export replicaObj.setNeedsCopyToTarget(true); } replicaObj.setSyncActive(inSync); replicaObj.setTechnologyType(BlockSnapshot.TechnologyType.NATIVE.name()); // We check to see if the snapshot target volume is linked to an unsupported // synchronization aspect. The unsupported synchronization aspects are those // aspects that share the same name, likely because they are VMAX3 SnapVx // aspects that utilize generation numbers to differentiate array snapshots // with the same name. The names of these aspects are in the duplicate // synchronization aspect element names map. If the snapshot relationship // name maps to a duplicate name, then we set the aspect path, i.e., the // settingsInstance to a value that indicates the snapshot is linked an invalid // aspect. In the volume discovery post processor, we will check this value // for each snapshot and if it is set to this specific invalid value, we will // mark the UnManagedVolume instance representing the snapshot as not ingestable. String relationshipName = getCIMPropertyValue(instance, EMC_RELATIONSHIP_NAME); Set<String> duplicateElementNamesForSrc = _duplicateSyncAspectElementNameMap.get(srcNativeGuid); if ((duplicateElementNamesForSrc != null) && (duplicateElementNamesForSrc.contains(relationshipName))) { _logger.info("Processed snapshot {} linked to unsupported synchronization aspect with name {}", nativeGuid, relationshipName); replicaObj.setSettingsInstance(Constants.NOT_INGESTABLE_SYNC_ASPECT); } else { Map<String, String> aspectsForSource = _syncAspectMap.get(srcNativeGuid); if (null != aspectsForSource) { String aspectKey = getSyncAspectMapKey(srcNativeGuid, relationshipName); if (aspectsForSource.containsKey(aspectKey)) { String syncAspect = aspectsForSource.get(aspectKey); replicaObj.setSettingsInstance(syncAspect); } } } if (null == srcReplicaObj.getSnapshots()) { srcReplicaObj.setSnapshots(new StringSet()); } srcReplicaObj.getSnapshots().add(nativeGuid); } else if (SYNC_TYPE_CLONE.equals(syncType) || (SYNC_TYPE_MIRROR.equals(syncType) && // On VNX, full copies are actually mirrors // Mirrors with restorable states (fractured and split, except synchronized) are treated as full copies, // while mirrors with synchronized and other states are treated as mirrors systemName.toLowerCase().startsWith(Constants.CLARIION) && (copyState.equals(COPY_STATE_FRACTURED) || copyState.equals(COPY_STATE_SPLIT)))) { replicaObj.setType(LocalReplicaObject.Types.FullCopy); replicaObj.setSyncActive(inSync); replicaObj.setReplicaState(isReplicaOfSnapshot ? SNAPSHOT_CLONE_REPLICA_STATE : getReplicaState(copyState)); if (null == srcReplicaObj.getFullCopies()) { srcReplicaObj.setFullCopies(new StringSet()); } srcReplicaObj.getFullCopies().add(nativeGuid); } else if (SYNC_TYPE_MIRROR.equals(syncType)) { replicaObj.setType(LocalReplicaObject.Types.BlockMirror); replicaObj.setSyncType(syncType); replicaObj.setSyncState(syncState); replicaObj.setSynchronizedInstance(createStorageSynchronizedObjPath(targetPath, sourcePath)); if (null == srcReplicaObj.getMirrors()) { srcReplicaObj.setMirrors(new StringSet()); } srcReplicaObj.getMirrors().add(nativeGuid); } else { _logger.debug("Ignore Target {}, SyncType {}", nativeGuid, syncType); } } catch (Exception e) { _logger.error("Exception on processing instances", e); } } } /** * create object path from source and target paths * * @param targetPath * @param sourcePath * @return String */ private String createStorageSynchronizedObjPath(CIMObjectPath targetPath, CIMObjectPath sourcePath) { @SuppressWarnings("rawtypes") CIMProperty[] propKeys = { new CIMProperty<String>(SmisConstants.CP_SYNCED_ELEMENT, CIMDataType.STRING_T, targetPath.toString(), true, false, null), new CIMProperty<String>(SmisConstants.CP_SYSTEM_ELEMENT, CIMDataType.STRING_T, sourcePath.toString(), true, false, null), }; return CimObjectPathCreator.createInstance( Constants.STORAGE_SYNCHRONIZED_SV_SV, Constants.EMC_NAMESPACE, propKeys).toString(); } /* * Translate CopyState to ViPR ReplicationState for clone * * Clone can be restored if it is ReplicationState.SYNCHRONIZED * corresponding CopyState values are synchronized, fractured and split */ private String getReplicaState(String copyState) { switch (copyState) { case COPY_STATE_SYNCHRONIZED: case COPY_STATE_FRACTURED: case COPY_STATE_SPLIT: return Volume.ReplicationState.SYNCHRONIZED.name(); case COPY_STATE_INACTIVE: return Volume.ReplicationState.INACTIVE.name(); default: return Volume.ReplicationState.UNKNOWN.name(); } } /** * Returns true if the given syncType and/or copyMethod is determined to represent a snapshot. * * @param syncType Value of a SyncType property. * @param copyMethod Value of a CopyMethodology property. * @return true, if they represent a snapshot, false otherwise. */ private boolean isReplicationSnapshot(String syncType, String copyMethod) { return SYNC_TYPE_SNAPSHOT.equals(syncType) || (SYNC_TYPE_CLONE.equals(syncType) && SNAPVX_COPY_METHODOLOGY.equals(copyMethod)); } }