/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.hds.prov; import java.net.URI; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockMirror; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.NameGenerator; import com.emc.storageos.exceptions.DeviceControllerExceptions; import com.emc.storageos.hds.HDSConstants; import com.emc.storageos.hds.api.HDSApiClient; import com.emc.storageos.hds.api.HDSApiExportManager; import com.emc.storageos.hds.api.HDSApiFactory; import com.emc.storageos.hds.api.HDSApiProtectionManager; import com.emc.storageos.hds.model.FreeLun; import com.emc.storageos.hds.model.HostStorageDomain; import com.emc.storageos.hds.model.Path; import com.emc.storageos.hds.model.ReplicationInfo; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.volumecontroller.ControllerLockingService; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.BlockMirrorCreateCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.BlockSnapshotCreateCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeCreateCompleter; import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSBlockCreateMirrorJob; import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSBlockCreateSnapshotJob; import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSCreateVolumeJob; import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSDeleteSnapshotJob; import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSJob; import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSCommandHelper; import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSUtils; public class HDSProtectionOperations { private static final Logger log = LoggerFactory.getLogger(HDSProtectionOperations.class); private DbClient dbClient; private HDSApiFactory hdsApiFactory; private HDSCommandHelper hdsCommandHelper; protected NameGenerator nameGenerator; private ControllerLockingService locker; /** * key storageSystemObjId, * Value, lock instance */ private Map<String, ReentrantLock> localJVMLockMap = new HashMap<String, ReentrantLock>(); public DbClient getDbClient() { return dbClient; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public HDSApiFactory getHdsApiFactory() { return hdsApiFactory; } public void setHdsApiFactory(HDSApiFactory hdsApiFactory) { this.hdsApiFactory = hdsApiFactory; } public HDSCommandHelper getHdsCommandHelper() { return hdsCommandHelper; } public void setHdsCommandHelper(HDSCommandHelper hdsCommandHelper) { this.hdsCommandHelper = hdsCommandHelper; } public void setNameGenerator(NameGenerator nameGenerator) { this.nameGenerator = nameGenerator; } /** * Creates secondary volume for ShadowImage pair operations. * * @param storageSystem * @param sourceVolume * @param targetVolume * @throws Exception */ public void createSecondaryVolumeForClone(StorageSystem storageSystem, URI sourceVolume, Volume targetVolume) throws Exception { log.info("SecondaryVolume creation operation started"); String taskId = UUID.randomUUID().toString(); TaskCompleter taskCompleter = new VolumeCreateCompleter(targetVolume.getId(), taskId); String asyncTaskMessageId = null; HDSApiClient hdsApiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); String systemObjectID = HDSUtils.getSystemObjectID(storageSystem); BlockObject sourceObj = BlockObject.fetch(dbClient, sourceVolume); URI tenantUri = null; StoragePool targetPool = dbClient.queryObject(StoragePool.class, targetVolume.getPool()); if (sourceObj instanceof BlockSnapshot) { // In case of snapshot, get the tenant from its parent volume NamedURI parentVolUri = ((BlockSnapshot) sourceObj).getParent(); Volume parentVolume = dbClient.queryObject(Volume.class, parentVolUri); tenantUri = parentVolume.getTenant().getURI(); TenantOrg tenantOrg = dbClient.queryObject(TenantOrg.class, tenantUri); // String cloneLabel = generateLabel(tenantOrg, cloneObj); } else { // This is a default flow tenantUri = ((Volume) sourceObj).getTenant().getURI(); } if (targetVolume.getThinlyProvisioned()) { asyncTaskMessageId = hdsApiClient.createThinVolumes(systemObjectID, targetPool.getNativeId(), targetVolume.getCapacity(), 1, targetVolume.getLabel(), HDSConstants.QUICK_FORMAT_TYPE, storageSystem.getModel()); } else { String poolObjectID = HDSUtils.getPoolObjectID(targetPool); asyncTaskMessageId = hdsApiClient.createThickVolumes(systemObjectID, poolObjectID, targetVolume.getCapacity(), 1, targetVolume.getLabel(), null, storageSystem.getModel(), null); } if (asyncTaskMessageId != null) { HDSJob createHDSJob = new HDSCreateVolumeJob( asyncTaskMessageId, targetVolume.getStorageController(), targetPool.getId(), taskCompleter); hdsCommandHelper.waitForAsyncHDSJob(createHDSJob); } log.info("SecondaryVolume creation operation completed successfully"); } /** * Creates secondary volume for ShadowImage pair operations. * * @param storageSystem * @param sourceVolume * @param mirror * @throws Exception */ public void createSecondaryVolumeForMirror(StorageSystem storageSystem, URI sourceVolume, BlockMirror mirror) throws Exception { log.info("SecondaryVolume for mirror creation operation started"); String taskId = UUID.randomUUID().toString(); TaskCompleter taskCompleter = new BlockMirrorCreateCompleter(mirror.getId(), taskId); String asyncTaskMessageId = null; HDSApiClient hdsApiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); String systemObjectID = HDSUtils.getSystemObjectID(storageSystem); StoragePool targetPool = dbClient.queryObject(StoragePool.class, mirror.getPool()); Volume source = dbClient.queryObject(Volume.class, sourceVolume); TenantOrg tenant = dbClient.queryObject(TenantOrg.class, source.getTenant().getURI()); String tenantName = tenant.getLabel(); String targetLabelToUse = nameGenerator.generate(tenantName, mirror.getLabel(), mirror.getId().toString(), '-', HDSConstants.MAX_VOLUME_NAME_LENGTH); if (mirror.getThinlyProvisioned()) { asyncTaskMessageId = hdsApiClient.createThinVolumes(systemObjectID, targetPool.getNativeId(), mirror.getCapacity(), 1, targetLabelToUse, HDSConstants.QUICK_FORMAT_TYPE, storageSystem.getModel()); } else { String poolObjectID = HDSUtils.getPoolObjectID(targetPool); asyncTaskMessageId = hdsApiClient.createThickVolumes(systemObjectID, poolObjectID, mirror.getCapacity(), 1, targetLabelToUse, null, storageSystem.getModel(), null); } if (asyncTaskMessageId != null) { HDSJob createHDSJob = new HDSBlockCreateMirrorJob( asyncTaskMessageId, mirror.getStorageController(), targetPool.getId(), taskCompleter); hdsCommandHelper.waitForAsyncHDSJob(createHDSJob); } log.info("SecondaryVolume for mirror creation operation completed successfully"); } /** * Creates Snapshot Volume * * @param storageSystem * @param sourceVolume * @param snapshotObj * @throws Exception */ public void createSecondaryVolumeForSnapshot(StorageSystem storageSystem, Volume sourceVolume, BlockSnapshot snapshotObj) throws Exception { log.info("SecondaryVolume for snapshot creation operation started"); String taskId = UUID.randomUUID().toString(); TaskCompleter taskCompleter = new BlockSnapshotCreateCompleter(Arrays.asList(snapshotObj.getId()), taskId); String asyncTaskMessageId = null; HDSApiClient hdsApiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); String systemObjectID = HDSUtils.getSystemObjectID(storageSystem); asyncTaskMessageId = hdsApiClient.createSnapshotVolume(systemObjectID, sourceVolume.getCapacity(), storageSystem.getModel()); if (asyncTaskMessageId != null) { HDSJob createHDSJob = new HDSBlockCreateSnapshotJob( asyncTaskMessageId, snapshotObj.getStorageController(), taskCompleter); hdsCommandHelper.waitForAsyncHDSJob(createHDSJob); } log.info("SecondaryVolume for snapshot creation operation completed successfully"); } public void deleteSecondaryVolumeSnapshot(StorageSystem storageSystem, BlockSnapshot snapshotObj, TaskCompleter taskCompleter) throws Exception { log.info("Snapshot deletion operation started"); String asyncTaskMessageId = null; HDSApiClient hdsApiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); String systemObjectID = HDSUtils.getSystemObjectID(storageSystem); String logicalUnitObjId = HDSUtils.getLogicalUnitObjectId(snapshotObj.getNativeId(), storageSystem); asyncTaskMessageId = hdsApiClient.deleteSnapshotVolume(systemObjectID, logicalUnitObjId, storageSystem.getModel()); if (null != asyncTaskMessageId) { HDSJob deleteSnapshotJob = new HDSDeleteSnapshotJob(asyncTaskMessageId, snapshotObj.getStorageController(), taskCompleter); hdsCommandHelper.waitForAsyncHDSJob(deleteSnapshotJob); log.info("Snapshot deletion operation completed successfully"); } else { // This path should never be taken as the HDS client should always return // the asynchronous task id. If it does not, this will be treated as an // error. log.error("Unexpected null asynchronous task id from HDS client call to delete volume snapshot."); ServiceCoded sc = DeviceControllerExceptions.hds.nullAsyncTaskIdForDeleteSnapshot(snapshotObj.forDisplay()); taskCompleter.error(dbClient, sc); } } /** * Adds Dummy Lun Path to Secondary Volume for pair creation * * @param client * @param volume * @throws Exception */ public void addDummyLunPath(HDSApiClient client, BlockObject volume) throws Exception { StorageSystem system = dbClient.queryObject(StorageSystem.class, volume.getStorageController()); String systemObjectId = HDSUtils.getSystemObjectID(system); ReentrantLock lock = null; try { /** * This will take care synchronization between all cluster node. * So that same time two different node can not add same lun to different volumes. * This will not take care within the same node. */ locker.acquireLock(systemObjectId, HDSConstants.LOCK_WAIT_SECONDS); /** * We create and maintain lock instance per storage system. * So that multiple thread can not add same lun number to different volumes on same storage system. */ lock = getLock(systemObjectId); lock.lock(); log.info("Acquired Lock to add lun path"); HostStorageDomain hsd = getDummyHSDFromStorageSystem(client, systemObjectId); if (null == hsd) { log.info("Creating dummy HSD for ShadowImage"); // Get any port which belongs to the storage system. URIQueryResultList storagePortURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(system.getId()), storagePortURIs); StoragePort storagePort = null; Iterator<URI> storagePortsIter = storagePortURIs.iterator(); while (storagePortsIter.hasNext()) { URI storagePortURI = storagePortsIter.next(); storagePort = dbClient.queryObject(StoragePort.class, storagePortURI); if (storagePort != null && !storagePort.getInactive()) { break; } } if (storagePort != null) { String portId = HDSUtils.getPortID(storagePort); hsd = client.getHDSApiExportManager().addHostStorageDomain(systemObjectId, portId, HDSConstants.HOST_GROUP_DOMAIN_TYPE, null, HDSConstants.DUMMY_HSD, null, null, system.getModel()); log.info("Created dummy HSD on {} portid", portId); } } List<FreeLun> freeLunList = client.getHDSApiExportManager().getFreeLUNInfo(systemObjectId, hsd.getObjectID()); log.debug("freeLunList.size :{}", freeLunList.size()); log.debug("Free lun:{}", freeLunList.get(0).getLun()); Map<String, String> deviceLunList = new HashMap<String, String>(); deviceLunList.put(volume.getNativeId(), freeLunList.get(0).getLun()); client.getHDSApiExportManager().addLUN(systemObjectId, hsd.getPortID(), hsd.getDomainID(), deviceLunList, system.getModel()); log.info("Completed addDummyLunPath method"); } finally { if (lock != null) { lock.unlock(); log.info("Released Lock to add lun path"); } locker.releaseLock(systemObjectId); } } /** * Check lock is already available for the give array. * if not available create a new one. * * @param systemObjId * @return */ private ReentrantLock getLock(String systemObjId) { ReentrantLock lock = null; synchronized (localJVMLockMap) { lock = localJVMLockMap.get(systemObjId); if (lock == null) { lock = new ReentrantLock(); localJVMLockMap.put(systemObjId, lock); } } return lock; } /** * Get DummyHSD from StorageSystem if exist * * @param apiClient * @param systemObjectId * @return dummyHSD * @throws Exception */ private HostStorageDomain getDummyHSDFromStorageSystem(HDSApiClient apiClient, String systemObjectId) throws Exception { List<HostStorageDomain> hsdList = apiClient.getHDSApiExportManager().getHostStorageDomains(systemObjectId); if (hsdList != null) { log.debug("HSD list size :{}", hsdList.size()); for (HostStorageDomain hsd : hsdList) { if (hsd != null && HDSConstants.DUMMY_HSD.equalsIgnoreCase(hsd.getNickname())) { log.info("Found ViPR dummy HSD on storage system"); return hsd; } } } return null; } /** * Deletes shadowImage Pair * * @param storageSystem * @param source * @param target * @throws Exception */ public void deleteShadowImagePair(StorageSystem storageSystem, Volume source, Volume target) throws Exception { log.info("Delete pair operation started"); HDSApiClient apiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); HDSApiProtectionManager apiProtectionManager = apiClient.getHdsApiProtectionManager(); Map<String, String> repliMap = apiProtectionManager. getReplicationRelatedObjectIds(source.getNativeId(), target.getNativeId()); log.info("Replication Obj Ids :{}", repliMap); String replicationGroupObjId = repliMap.get(HDSConstants.REPLICATION_GROUP_OBJ_ID); String replicationInfoObjId = repliMap.get(HDSConstants.REPLICATION_INFO_OBJ_ID); apiProtectionManager.deleteShadowImagePair(replicationGroupObjId, replicationInfoObjId, storageSystem.getModel()); log.info("Delete pair operation completed"); } /** * Modifies pair operation to split|resync|restore * * @param storageSystem * @param sourceVolumeNativeId * @param targetVolumeNativeId * @param operationType * @throws Exception */ public boolean modifyShadowImagePair(StorageSystem storageSystem, String sourceVolumeNativeId, String targetVolumeNativeId, HDSApiProtectionManager.ShadowImageOperationType operationType) throws Exception { HDSApiClient apiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); HDSApiProtectionManager apiProtectionManager = apiClient.getHdsApiProtectionManager(); log.info("{} pair operation started", operationType.name()); Map<String, String> repliMap = apiProtectionManager. getReplicationRelatedObjectIds(sourceVolumeNativeId, targetVolumeNativeId); log.info("Replication Obj Ids :{}", repliMap); String replicationGroupObjId = repliMap.get(HDSConstants.REPLICATION_GROUP_OBJ_ID); String replicationInfoObjId = repliMap.get(HDSConstants.REPLICATION_INFO_OBJ_ID); ReplicationInfo replicationInfo = apiProtectionManager.modifyShadowImagePair(replicationGroupObjId, replicationInfoObjId, operationType, storageSystem.getModel()); log.info("{} pair operation completed", operationType.name()); return (replicationInfo != null); } /** * Removes Dummy Lun Path from Secondary Volume * * @param storageSystem * @param blockObjectURI * @throws Exception */ public void removeDummyLunPath(StorageSystem storageSystem, URI blockObjectURI) throws Exception { log.info("Started dummy lun path removal from secondary volume"); HDSApiClient apiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); HDSApiExportManager apiExportManager = apiClient.getHDSApiExportManager(); String systemObjectId = HDSUtils.getSystemObjectID(storageSystem); // Volume volume=dbClient.queryObject(Volume.class, volumeURI); BlockObject blockObj = BlockObject.fetch(dbClient, blockObjectURI); String dummyLunPathId = getDummyHSDPathId(storageSystem, blockObj); if (dummyLunPathId != null) { apiExportManager.deleteLunPathsFromSystem(systemObjectId, Arrays.asList(dummyLunPathId), storageSystem.getModel()); log.info("Deleted Dummy Lun path from secondary volume"); } else { log.info("Dummy lun path has been removed already"); } } /** * Get Dummy Lun Path's path objectID * * @param storageSystem * @param volume * @return * @throws Exception */ private String getDummyHSDPathId(StorageSystem storageSystem, BlockObject blockObj) throws Exception { String dummyLunPathId = null; HDSApiClient apiClient = HDSUtils.getHDSApiClient(hdsApiFactory, storageSystem); HDSApiExportManager apiExportManager = apiClient.getHDSApiExportManager(); String systemObjectId = HDSUtils.getSystemObjectID(storageSystem); List<HostStorageDomain> hsdList = apiExportManager.getHostStorageDomains(systemObjectId); if (hsdList != null) { for (HostStorageDomain hsd : hsdList) { if (hsd != null && HDSConstants.DUMMY_HSD.equalsIgnoreCase( hsd.getNickname())) { if (hsd.getPathList() != null) { for (Path path : hsd.getPathList()) { if (path.getDevNum().equalsIgnoreCase(blockObj.getNativeId())) { dummyLunPathId = path.getObjectID(); log.info("Found secondary volume's dummy lun path id :{}", dummyLunPathId); return dummyLunPathId; } } } } } } log.info("Dummy lun path has been removed already for this secondary volume"); return dummyLunPathId; } /** * Returns pair name based on source and target volume's nativeId * * @param source * @param target * @return pairName Ex: 100_104_SI */ public String generatePairName(BlockObject source, BlockObject target) { StringBuilder pairName = new StringBuilder(); pairName.append(source.getNativeId()); pairName.append(HDSConstants.UNDERSCORE_OPERATOR); pairName.append(target.getNativeId()); pairName.append(HDSConstants.UNDERSCORE_OPERATOR); pairName.append(HDSConstants.SI); return pairName.toString(); } /** * Set the controller locking service. * * @param locker An instance of ControllerLockingService */ public void setLocker(ControllerLockingService locker) { this.locker = locker; } }