/* * Copyright (c) 2008-2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.blockingestorchestration; import java.net.URI; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.IngestStrategyFactory.IngestStrategyEnum; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.IngestStrategyFactory.ReplicationStrategy; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.IngestStrategyFactory.VolumeType; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.impl.VplexVolumeIngestionContext; import com.emc.storageos.api.service.impl.resource.utils.PropertySetterUtil; import com.emc.storageos.api.service.impl.resource.utils.VolumeIngestionUtil; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.BlockSnapshotSession; 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.StoragePool; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.VirtualPool; 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.SupportedVolumeInformation; /** * Responsible for ingesting block local volumes. */ public class BlockVolumeIngestOrchestrator extends BlockIngestOrchestrator { private static final Logger _logger = LoggerFactory.getLogger(BlockVolumeIngestOrchestrator.class); @Override protected <T extends BlockObject> T ingestBlockObjects(IngestionRequestContext requestContext, Class<T> clazz) throws IngestionException { UnManagedVolume unManagedVolume = requestContext.getCurrentUnmanagedVolume(); boolean unManagedVolumeExported = requestContext.getVolumeContext().isVolumeExported(); Volume volume = null; List<BlockSnapshotSession> snapSessions = new ArrayList<BlockSnapshotSession>(); URI unManagedVolumeUri = unManagedVolume.getId(); String volumeNativeGuid = unManagedVolume.getNativeGuid().replace(VolumeIngestionUtil.UNMANAGEDVOLUME, VolumeIngestionUtil.VOLUME); volume = VolumeIngestionUtil.checkIfVolumeExistsInDB(volumeNativeGuid, _dbClient); // Check if ingested volume has export masks pending for ingestion. if (isExportIngestionPending(volume, unManagedVolumeUri, unManagedVolumeExported)) { return clazz.cast(volume); } if (null == volume) { validateUnManagedVolume(unManagedVolume, requestContext.getVpool(unManagedVolume)); // @TODO Need to revisit this. In 8.x Provider, ReplicationGroup is automatically created when a volume is associated to a // StorageGroup. // checkUnManagedVolumeAddedToCG(unManagedVolume, virtualArray, tenant, project, vPool); checkVolumeExportState(unManagedVolume, unManagedVolumeExported); checkVPoolValidForExportInitiatorProtocols(requestContext.getVpool(unManagedVolume), unManagedVolume); checkHostIOLimits(requestContext.getVpool(unManagedVolume), unManagedVolume, unManagedVolumeExported); StoragePool pool = validateAndReturnStoragePoolInVAarray(unManagedVolume, requestContext.getVarray(unManagedVolume)); // validate quota is exceeded for storage systems and pools checkSystemResourceLimitsExceeded(requestContext.getStorageSystem(), unManagedVolume, requestContext.getExhaustedStorageSystems()); checkPoolResourceLimitsExceeded(requestContext.getStorageSystem(), pool, unManagedVolume, requestContext.getExhaustedPools()); String autoTierPolicyId = getAutoTierPolicy(unManagedVolume, requestContext.getStorageSystem(), requestContext.getVpool(unManagedVolume)); validateAutoTierPolicy(autoTierPolicyId, unManagedVolume, requestContext.getVpool(unManagedVolume)); volume = createVolume(requestContext, volumeNativeGuid, pool, unManagedVolume, autoTierPolicyId); } if (volume != null) { String syncActive = PropertySetterUtil.extractValueFromStringSet( SupportedVolumeInformation.IS_SYNC_ACTIVE.toString(), unManagedVolume.getVolumeInformation()); boolean isSyncActive = (null != syncActive) ? Boolean.parseBoolean(syncActive) : false; volume.setSyncActive(isSyncActive); if (VolumeIngestionUtil.isFullCopy(unManagedVolume)) { _logger.info("Setting clone related properties {}", unManagedVolume.getId()); String replicaState = PropertySetterUtil.extractValueFromStringSet( SupportedVolumeInformation.REPLICA_STATE.toString(), unManagedVolume.getVolumeInformation()); volume.setReplicaState(replicaState); String replicationGroupName = PropertySetterUtil.extractValueFromStringSet( SupportedVolumeInformation.FULL_COPY_CONSISTENCY_GROUP_NAME.toString(), unManagedVolume.getVolumeInformation()); if (replicationGroupName != null && !replicationGroupName.isEmpty()) { volume.setReplicationGroupInstance(replicationGroupName); } } // Create snapshot sessions for each synchronization aspect for the volume. StringSet syncAspectInfoForVolume = PropertySetterUtil.extractValuesFromStringSet( SupportedVolumeInformation.SNAPSHOT_SESSIONS.toString(), unManagedVolume.getVolumeInformation()); if ((syncAspectInfoForVolume != null) && (!syncAspectInfoForVolume.isEmpty())) { Project project = requestContext.getProject(); // If this is a vplex backend volume, then the front end project should be set as snapshot session's project if (requestContext instanceof VplexVolumeIngestionContext && VolumeIngestionUtil.isVplexBackendVolume(unManagedVolume)) { project = ((VplexVolumeIngestionContext) requestContext).getFrontendProject(); } for (String syncAspectInfo : syncAspectInfoForVolume) { String[] syncAspectInfoComponents = syncAspectInfo.split(":"); String syncAspectName = syncAspectInfoComponents[0]; String syncAspectObjPath = syncAspectInfoComponents[1]; // Make sure it is not already created. URIQueryResultList queryResults = new URIQueryResultList(); _dbClient.queryByConstraint(AlternateIdConstraint.Factory.getBlockSnapshotSessionBySessionInstance(syncAspectObjPath), queryResults); Iterator<URI> queryResultsIter = queryResults.iterator(); if (!queryResultsIter.hasNext()) { BlockSnapshotSession session = new BlockSnapshotSession(); session.setId(URIUtil.createId(BlockSnapshotSession.class)); session.setLabel(syncAspectName); session.setSessionLabel(syncAspectName); session.setParent(new NamedURI(volume.getId(), volume.getLabel())); session.setProject(new NamedURI(project.getId(), project.getLabel())); session.setStorageController(volume.getStorageController()); session.setSessionInstance(syncAspectObjPath); StringSet linkedTargetURIs = new StringSet(); URIQueryResultList snapshotQueryResults = new URIQueryResultList(); _dbClient.queryByConstraint(AlternateIdConstraint.Factory.getBlockSnapshotBySettingsInstance(syncAspectObjPath), snapshotQueryResults); Iterator<URI> snapshotQueryResultsIter = snapshotQueryResults.iterator(); while (snapshotQueryResultsIter.hasNext()) { linkedTargetURIs.add(snapshotQueryResultsIter.next().toString()); } session.setLinkedTargets(linkedTargetURIs); session.setOpStatus(new OpStatusMap()); snapSessions.add(session); } } if (!snapSessions.isEmpty()) { _dbClient.createObject(snapSessions); } } } // Note that a VPLEX backend volume can also be a snapshot target volume. // When the VPLEX ingest orchestrator is executed, it gets the ingestion // strategy for the backend volume and executes it. If the backend volume // is both a snapshot and a VPLEX backend volume, this local volume ingest // strategy is invoked and a Volume instance will result. That is fine because // we need to represent that VPLEX backend volume. However, we also need a // BlockSnapshot instance to represent the snapshot target volume. Therefore, // if the unmanaged volume is also a snapshot target volume, we get and // execute the local snapshot ingest strategy to create this BlockSnapshot // instance and we add it to the created object list. Note that since the // BlockSnapshot is added to the created objects list and the Volume and // BlockSnapshot instance will have the same native GUID, we must be careful // about adding the Volume to the created object list in the VPLEX ingestion // strategy. BlockObject snapshot = null; if (VolumeIngestionUtil.isSnapshot(unManagedVolume)) { String strategyKey = ReplicationStrategy.LOCAL.name() + "_" + VolumeType.SNAPSHOT.name(); IngestStrategy ingestStrategy = ingestStrategyFactory.getIngestStrategy(IngestStrategyEnum.getIngestStrategy(strategyKey)); snapshot = ingestStrategy.ingestBlockObjects(requestContext, BlockSnapshot.class); requestContext.getBlockObjectsToBeCreatedMap().put(snapshot.getNativeGuid(), snapshot); } // Run this always when volume NO_PUBLIC_ACCESS if (markUnManagedVolumeInactive(requestContext, volume)) { _logger.info("All the related replicas and parent has been ingested ", unManagedVolume.getNativeGuid()); // mark inactive if this is not to be exported. Else, mark as // inactive after successful export. Do not mark inactive for RP volumes because the RP masks should still be ingested // even though they are not exported to host/cluster. UnManaged RP volumes will be marked inactive after successful ingestion of // RP masks. if (!unManagedVolumeExported && !VolumeIngestionUtil.checkUnManagedResourceIsRecoverPointEnabled(unManagedVolume)) { unManagedVolume.setInactive(true); requestContext.getUnManagedVolumesToBeDeleted().add(unManagedVolume); } } else if (volume != null) { _logger.info( "Not all the parent/replicas of unManagedVolume {} have been ingested , hence marking as internal", unManagedVolume.getNativeGuid()); volume.addInternalFlags(INTERNAL_VOLUME_FLAGS); for (BlockSnapshotSession snapSession : snapSessions) { snapSession.addInternalFlags(INTERNAL_VOLUME_FLAGS); } _dbClient.updateObject(snapSessions); } return clazz.cast(volume); } @Override protected void validateAutoTierPolicy(String autoTierPolicyId, UnManagedVolume unManagedVolume, VirtualPool vPool) { String associatedSourceVolume = PropertySetterUtil.extractValueFromStringSet( SupportedVolumeInformation.LOCAL_REPLICA_SOURCE_VOLUME.toString(), unManagedVolume.getVolumeInformation()); // Skip autotierpolicy validation for clones as we use same orchestration for both volume & clone. if (null != associatedSourceVolume) { return; } else { super.validateAutoTierPolicy(autoTierPolicyId, unManagedVolume, vPool); } } /** * Following steps are performed as part of this method execution. * * @TODO refactor the code to modularize responsibilities. * * 1. Checks whether unManagedVolume is protected by RP or VPLEX, if yes we willn't create backend CG. * 2. For regular volumes in unManaged CG, we will create CG when ingesting last volume in unmanaged CG. * 3. When ingesting last regular volume in unmanaged CG, we will check whether CG already exists in DB for the same project & * tenant. * If yes, we will reuse it. * Otherwise, we will create new BlockConsistencyGroup for the unmanaged consistencyGroup. * */ @Override protected BlockConsistencyGroup getConsistencyGroup(UnManagedVolume unManagedVolume, BlockObject blockObj, IngestionRequestContext context, DbClient dbClient) { if (VolumeIngestionUtil.checkUnManagedResourceAddedToConsistencyGroup(unManagedVolume)) { return VolumeIngestionUtil.getBlockObjectConsistencyGroup(unManagedVolume, blockObj, context, dbClient); } return null; } }