/* * Copyright (c) 2014-2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.processor.detailedDiscovery; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume.SupportedVolumeCharacterstics; import com.emc.storageos.plugins.BaseCollectionException; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.plugins.common.PartitionManager; import com.emc.storageos.plugins.common.domainmodel.Operation; import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator; import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.processor.StorageProcessor; import com.emc.storageos.volumecontroller.impl.utils.DiscoveryUtils; import com.google.common.base.Joiner; /* * This is used to set supported VPools for local replicas * The supported VPools of replicas will be the same as that of the source. * This should be executed at the end of the unmanaged volume discovery. * * Also filter out snapshots of snapshot by setting INGESTABLE to false. */ public class VolumeDiscoveryPostProcessor extends StorageProcessor { private static final Logger _logger = LoggerFactory .getLogger(VolumeDiscoveryPostProcessor.class); private static final String FALSE = "false"; private PartitionManager _partitionManager; public void runReplicaPostProcessing(Map<String, LocalReplicaObject> volumeToReplicaMap, DbClient dbClient) { setSupportedVPoolsForReplicas(volumeToReplicaMap, dbClient); filterUnsupportedSnapshots(volumeToReplicaMap, dbClient); } private void setSupportedVPoolsForReplicas( Map<String, LocalReplicaObject> volumeToReplicaMap, DbClient dbClient) { _logger.info("Post processing UnManagedVolumes setSupportedVPoolsForReplicas"); List<UnManagedVolume> modifiedUnManagedVolumes = new ArrayList<UnManagedVolume>(); // for each source, set SUPPORTED_VPOOL_LIST for its targets for (Entry<String, LocalReplicaObject> entry : volumeToReplicaMap.entrySet()) { String srcNativeGuid = entry.getKey(); LocalReplicaObject srcObj = entry.getValue(); if (srcObj.hasReplica() && !srcObj.isReplica()) { // process its dependents try { StringSet vPools = null; // check if unmanaged volume is created UnManagedVolume unManagedVolume = checkUnManagedVolumeExistsInDB(srcNativeGuid, dbClient); if (unManagedVolume != null) { vPools = unManagedVolume.getSupportedVpoolUris(); } else { // check if it has already been ingested String volumeNativeGuid = srcNativeGuid.replace(NativeGUIDGenerator.UN_MANAGED_VOLUME, NativeGUIDGenerator.VOLUME); Volume volume = checkStorageVolumeExistsInDB(volumeNativeGuid, dbClient); if (volume != null) { _logger.debug("Volume {} is already being managed by ViPR", volumeNativeGuid); vPools = DiscoveryUtils.getMatchedVirtualPoolsForPool(dbClient, volume.getPool(), volume.getThinlyProvisioned().toString(), unManagedVolume); } } if (vPools != null && !vPools.isEmpty()) { setVPoolsForDependents(vPools, srcObj, volumeToReplicaMap, modifiedUnManagedVolumes, dbClient); } else { _logger.info("Cannot find supported VPools for {}", srcNativeGuid); } } catch (Exception e) { _logger.warn("Exception on setVPoolsForReplicas {}", e.getMessage()); } } // if modifiedUnManagedVolumes size reaches BATCH_SIZE, persist to db if (modifiedUnManagedVolumes.size() >= BATCH_SIZE) { _partitionManager.updateAndReIndexInBatches(modifiedUnManagedVolumes, BATCH_SIZE, dbClient, "UnManagedVolumes"); modifiedUnManagedVolumes.clear(); } } if (!modifiedUnManagedVolumes.isEmpty()) { _partitionManager.updateAndReIndexInBatches(modifiedUnManagedVolumes, BATCH_SIZE, dbClient, "UnManagedVolumes"); } } /* * Set VPools of replicas recursively */ private void setVPoolsForDependents(StringSet vPools, LocalReplicaObject srcObj, Map<String, LocalReplicaObject> volumeToReplicaMap, List<UnManagedVolume> unMangedVolumesUpdate, DbClient dbClient) { StringSet replicas = srcObj.getReplicas(); if (replicas != null && !replicas.isEmpty()) { for (String replica : replicas) { try { // get UnManagedVolume of replica and set SUPPORTED_VPOOL_LIST UnManagedVolume unManagedVolume = checkUnManagedVolumeExistsInDB(replica, dbClient); if (unManagedVolume != null) { _logger.debug("{} matched vpools: {}", unManagedVolume.getNativeGuid(), vPools); unManagedVolume.getSupportedVpoolUris().replace(vPools); unMangedVolumesUpdate.add(unManagedVolume); _logger.debug("Set VPools for {} to {}", replica, Joiner.on("\t").join(unManagedVolume.getSupportedVpoolUris())); LocalReplicaObject replicaObj = volumeToReplicaMap.get(replica); if (replicaObj.hasReplica()) { // process dependents setVPoolsForDependents(vPools, replicaObj, volumeToReplicaMap, unMangedVolumesUpdate, dbClient); } } else { // shouldn't happen _logger.warn("Cannot find unmanged volume {}", replica); } } catch (IOException e) { _logger.error("Exception on setVPoolsForDependents {}", e.getMessage()); } } } } private void filterUnsupportedSnapshots( Map<String, LocalReplicaObject> volumeToReplicaMap, DbClient dbClient) { _logger.info("Post processing UnManagedVolumes filterNestedSnapshots"); List<UnManagedVolume> modifiedUnManagedVolumes = new ArrayList<UnManagedVolume>(); for (Entry<String, LocalReplicaObject> entry : volumeToReplicaMap.entrySet()) { String nativeGuid = entry.getKey(); LocalReplicaObject obj = entry.getValue(); // Process each snapshot if (LocalReplicaObject.Types.BlockSnapshot.equals(obj.getType())) { try { UnManagedVolume unManagedVolume = checkUnManagedVolumeExistsInDB(nativeGuid, dbClient); // If the snapshot synchronization path indicates the snapshot target volume // is linked to an unsupported synchronization aspect, then the snapshot is not // ingestable. String syncAspectPath = obj.getSettingsInstance(); if (Constants.NOT_INGESTABLE_SYNC_ASPECT.equals(syncAspectPath)) { unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_INGESTABLE.name(), FALSE); unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_NOT_INGESTABLE_REASON.name(), "The snapshot cannot be ingested because the snapshot target volume is linked to an unsupported " + "array snapshot whose name is used by multiple array snapshots for the same source volume. The " + "storage system may use generation numbers to differentiate these snapshots, and the controller " + "does not currently support generation numbers"); modifiedUnManagedVolumes.add(unManagedVolume); } else { // If a snapshot has its own snapshot targets, then the snapshot target and all its // snapshot targets are non ingestable StringSet targets = obj.getSnapshots(); if (targets != null && !targets.isEmpty()) { if (unManagedVolume != null) { _logger.info( "Set UnManagedVolume {} for {} to non ingestable, this snapshot target is the source of other snapshot targets.", unManagedVolume.getId(), nativeGuid); unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_INGESTABLE.name(), FALSE); unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_NOT_INGESTABLE_REASON.name(), "The snapshot cannot be ingested because the snapshot is the source for a cascaded snapshot, " + "which is not currently supported by the controller"); modifiedUnManagedVolumes.add(unManagedVolume); } else { _logger.warn("No UnManagedVolume found for {}", nativeGuid); } // set all its snapshot targets to non ingestable since they are snapshot targets of a snapshot target for (String tgtNativeId : targets) { UnManagedVolume tgtUnManagedVolume = checkUnManagedVolumeExistsInDB(tgtNativeId, dbClient); if (tgtUnManagedVolume != null) { _logger.info( "Set UnManagedVolume {} for {} to non ingestable, the source of this snapshot target is also a snapshot target.", unManagedVolume.getId(), tgtNativeId); tgtUnManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_INGESTABLE.name(), FALSE); tgtUnManagedVolume.getVolumeCharacterstics().put( SupportedVolumeCharacterstics.IS_NOT_INGESTABLE_REASON.name(), "The snapshot cannot be ingested because it is a cascaded snapshot which is not currently " + "supported by the controller"); modifiedUnManagedVolumes.add(tgtUnManagedVolume); } else { _logger.warn("No UnManagedVolume found for {}", tgtNativeId); } } } } } catch (Exception e) { _logger.warn("Exception on filterNestedSnapshots {}", e.getMessage()); } } // if modifiedUnManagedVolumes size reaches BATCH_SIZE, persist to db if (modifiedUnManagedVolumes.size() >= BATCH_SIZE) { _partitionManager.updateAndReIndexInBatches(modifiedUnManagedVolumes, BATCH_SIZE, dbClient, "UnManagedVolumes"); modifiedUnManagedVolumes.clear(); } } if (!modifiedUnManagedVolumes.isEmpty()) { _partitionManager.updateAndReIndexInBatches(modifiedUnManagedVolumes, BATCH_SIZE, dbClient, "UnManagedVolumes"); } } public void setPartitionManager(PartitionManager partitionManager) { _partitionManager = partitionManager; } @Override public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap) throws BaseCollectionException { // TODO Auto-generated method stub } }