/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.hds.prov; import java.net.URI; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.hds.HDSConstants; import com.emc.storageos.hds.HDSException; import com.emc.storageos.hds.api.HDSApiClient; import com.emc.storageos.hds.api.HDSApiFactory; import com.emc.storageos.hds.model.HDSHost; import com.emc.storageos.hds.model.Pool; import com.emc.storageos.hds.model.ReplicationInfo; import com.emc.storageos.hds.model.SnapshotGroup; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.volumecontroller.DefaultSnapshotOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSUtils; public class HDSSnapshotOperations extends DefaultSnapshotOperations { private static final Logger log = LoggerFactory.getLogger(HDSSnapshotOperations.class); private DbClient dbClient; private HDSApiFactory hdsApiFactory; private HDSProtectionOperations hdsProtectionOperations; public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public void setHdsApiFactory(HDSApiFactory hdsApiFactory) { this.hdsApiFactory = hdsApiFactory; } public void setHdsProtectionOperations( HDSProtectionOperations hdsProtectionOperations) { this.hdsProtectionOperations = hdsProtectionOperations; } /** * Creates ThinImage instance on HDS. * 1. Find pair management server. * 2. Find ViPR-Snapshot-Group instance from storage system * 3. Find ThinImage pool. * 4. Create Snapshot instance on ThinImage Pool. * 5. Add DummyLunPath into Snapshot. * 6. Create ThinImage pair */ @Override public void createSingleVolumeSnapshot(StorageSystem storage, URI snapshot, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException { log.info("Create Single Volume Snapshot Started"); boolean isSnapshotCreated = false, isDummyLunPathAdded = false; HDSApiClient hdsApiClient = null; HDSHost pairMgmtServer = null; try { hdsApiClient = hdsApiFactory.getClient( HDSUtils.getHDSServerManagementServerInfo(storage), storage.getSmisUserName(), storage.getSmisPassword()); BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot); log.info("createSingleVolumeSnapshot operation START"); Volume volume = dbClient.queryObject(Volume.class, snapshotObj.getParent()); pairMgmtServer = hdsApiClient.getSnapshotGroupPairManagementServer(storage.getSerialNumber()); if (pairMgmtServer == null) { log.error("Unable to find snapshot group information/pair management server for Thin Image"); throw HDSException.exceptions.snapshotGroupNotAvailable(storage.getNativeGuid()); } String systemObjectId = HDSUtils.getSystemObjectID(storage); log.debug("StorageSystem Object Id :{}", systemObjectId); List<Pool> thinImagePoolList = hdsApiClient.getThinImagePoolList(systemObjectId); if (thinImagePoolList == null || thinImagePoolList.isEmpty()) { log.error("ThinImage Pool is not available on Storage System :{}", storage.getNativeGuid()); throw HDSException.exceptions.thinImagePoolNotAvailable(storage.getNativeGuid()); } Pool selectedThinImagePool = selectThinImagePoolForPlacement(thinImagePoolList, snapshotObj); if (selectedThinImagePool == null) { log.error("No ThinImage Pool is having enough free capcity to create snapshot on storage system :{}", storage.getNativeGuid()); throw HDSException.exceptions.notEnoughFreeCapacityOnthinImagePool(storage.getNativeGuid()); } // Create snapshot volume hdsProtectionOperations.createSecondaryVolumeForSnapshot(storage, volume, snapshotObj); isSnapshotCreated = true; snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot); // Add Dummy lun path hdsProtectionOperations.addDummyLunPath(hdsApiClient, snapshotObj); isDummyLunPathAdded = true; String snapShotGrpId = getViPRSnapshotGroup(pairMgmtServer, storage.getSerialNumber()).getObjectID(); // Create Thin Image pair hdsApiClient.createThinImagePair(snapShotGrpId, pairMgmtServer.getObjectID(), volume.getNativeId(), snapshotObj.getNativeId(), selectedThinImagePool.getPoolID(), storage.getModel()); taskCompleter.ready(dbClient); } catch (Exception e) { try { rollbackMethodForCreateSnapshot(isSnapshotCreated, isDummyLunPathAdded, hdsApiClient, storage, snapshot); } catch (Exception e1) { log.error("Exception occured while roll back snap creation", e1); } String errorMsg = String.format(CREATE_ERROR_MSG_FORMAT, snapshot); log.error(errorMsg, e); ServiceError serviceError = DeviceControllerErrors.hds.methodFailed("createSingleVolumeSnapshot", e.getMessage()); taskCompleter.error(dbClient, serviceError); } log.info("Create Single Volume Snapshot Completed"); } /** * Roll back method to clean up stale snapshot volume on storage system * * @param isSnapshotCreated * @param isDummyLunPathAdded * @param hdsApiClient * @param storage * @param snapshot * @throws Exception */ private void rollbackMethodForCreateSnapshot(boolean isSnapshotCreated, boolean isDummyLunPathAdded, HDSApiClient hdsApiClient, StorageSystem storage, URI snapshot) throws Exception { if (isDummyLunPathAdded) { log.info("Remove dummy path while doing roll back"); // Remove dummy lun path hdsProtectionOperations.removeDummyLunPath(storage, snapshot); } if (isSnapshotCreated) { log.info("Remove snapshot volume for roll back"); BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot); // Delete snapshot vollume String systemObjectID = HDSUtils.getSystemObjectID(storage); String logicalUnitObjId = HDSUtils.getLogicalUnitObjectId(snapshotObj.getNativeId(), storage); hdsApiClient.deleteSnapshotVolume(systemObjectID, logicalUnitObjId, storage.getModel()); } } private SnapshotGroup getViPRSnapshotGroup(HDSHost pairMgmtServer, String systemSerialNumber) { SnapshotGroup snapShotGrp = null; if (pairMgmtServer != null && pairMgmtServer.getSnapshotGroupList() != null) { for (SnapshotGroup snapshotGroup : pairMgmtServer.getSnapshotGroupList()) { if (snapshotGroup != null && HDSConstants.VIPR_SNAPSHOT_GROUP_NAME.equalsIgnoreCase(snapshotGroup.getGroupName()) && systemSerialNumber.equalsIgnoreCase(snapshotGroup.getSerialNumber())) { snapShotGrp = snapshotGroup; log.info("Snapshot Group Id :{}", snapShotGrp.getObjectID()); break; } } } return snapShotGrp; } private Pool selectThinImagePoolForPlacement(List<Pool> thinImagePoolList, BlockSnapshot snapshot) { Pool selectedPool = null; for (Pool pool : thinImagePoolList) { if (pool.getFreeCapacity() >= snapshot.getAllocatedCapacity()) { selectedPool = pool; log.info("ThinImage Pool {} has enough space to create snapshot", pool.getObjectID()); break; } } return selectedPool; } /** * Wrapper for setting the BlockSnapshot.inactive value * * @param snapshotURI [in] - BlockSnapshot object to update * @param value [in] - Value to assign to inactive */ protected void setInactive(URI snapshotURI, boolean value) { try { if (snapshotURI != null) { BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotURI); snapshot.setInactive(value); dbClient.persistObject(snapshot); } } catch (DatabaseException e) { log.error("IOException when trying to update snapshot.inactive value", e); } } /** * Wrapper for setting the BlockSnapshot.inactive value * * @param snapshotURIs [in] - List of BlockSnapshot objects to update * @param value [in] - Value to assign to inactive */ protected void setInactive(List<URI> snapshotURIs, boolean value) { try { if (snapshotURIs != null) { for (URI uri : snapshotURIs) { BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, uri); snapshot.setInactive(value); dbClient.persistObject(snapshot); } } } catch (DatabaseException e) { log.error("IOException when trying to update snapshot.inactive value", e); } } /** * 1. Delete ThinImage Pair * 2. Delete Dummy lun path from snap volume * 3. Delete Snapshot */ @Override public void deleteSingleVolumeSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { log.info("Delete Single Volume Snapshot Started"); try { BlockSnapshot snapshotObj = dbClient.queryObject(BlockSnapshot.class, snapshot); log.info("deleteSingleVolumeSnapshot operation START"); HDSApiClient hdsApiClient = hdsApiFactory.getClient( HDSUtils.getHDSServerManagementServerInfo(storage), storage.getSmisUserName(), storage.getSmisPassword()); // Get pair management server HDSHost pairMgmtServer = hdsApiClient.getSnapshotGroupPairManagementServer(storage.getSerialNumber()); if (null == pairMgmtServer) { log.error("Unable to find snapshot group information/pair management server for Thin Image"); throw HDSException.exceptions.snapshotGroupNotAvailable(storage.getNativeGuid()); } // Get snapshot group id SnapshotGroup snapshotGroup = getViPRSnapshotGroup(pairMgmtServer, storage.getSerialNumber()); String snapShotGrpId = snapshotGroup.getObjectID(); // Get replication object ids Volume volume = dbClient.queryObject(Volume.class, snapshotObj.getParent()); ReplicationInfo replicationInfo = getReplicationInfo(snapshotGroup, volume.getNativeId(), snapshotObj.getNativeId()); if (replicationInfo != null) { String replicationInfoObjId = replicationInfo.getObjectID(); // Delete ThinImage pair between volume and snapshot hdsApiClient.deleteThinImagePair(pairMgmtServer.getObjectID(), snapShotGrpId, replicationInfoObjId, storage.getModel()); } else { log.info("Pair has been deleted already on storage system"); } // Remove dummy lun path hdsProtectionOperations.removeDummyLunPath(storage, snapshot); // Delete snapshot vollume hdsProtectionOperations.deleteSecondaryVolumeSnapshot(storage, snapshotObj, taskCompleter); log.info("Delete Single Volume Snapshot Completed"); } catch (Exception e) { String errorMsg = String.format(DELETE_ERROR_MSG_FORMAT, snapshot); log.error(errorMsg, e); ServiceError serviceError = DeviceControllerErrors.hds.methodFailed("deleteSingleVolumeSnapshot", e.getMessage()); taskCompleter.error(dbClient, serviceError); } } /** * 1. Find pair management server. * 2. Get SnapshotGroup's Object Id. * 3. Get ReplicationInfo's Object Id. * 4. Perform ReplicationInfo Restore operation. */ @Override public void restoreSingleVolumeSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { BlockSnapshot from = dbClient.queryObject(BlockSnapshot.class, snapshot); Volume to = dbClient.queryObject(Volume.class, volume); HDSApiClient hdsApiClient = hdsApiFactory.getClient( HDSUtils.getHDSServerManagementServerInfo(storage), storage.getSmisUserName(), storage.getSmisPassword()); HDSHost pairMgmtServer = hdsApiClient.getSnapshotGroupPairManagementServer(storage.getSerialNumber()); if (pairMgmtServer == null) { log.error("Unable to find snapshot group information/pair management server for Thin Image"); throw HDSException.exceptions.snapshotGroupNotAvailable(storage.getNativeGuid()); } SnapshotGroup snapShotGrp = getViPRSnapshotGroup(pairMgmtServer, storage.getSerialNumber()); log.debug("to.getNativeId() :{}", to.getNativeId()); log.debug("from.getNativeId() :{}", from.getNativeId()); ReplicationInfo repInfo = getReplicationInfo(snapShotGrp, to.getNativeId(), from.getNativeId()); hdsApiClient.restoreThinImagePair(pairMgmtServer.getObjectID(), snapShotGrp.getObjectID(), repInfo.getObjectID(), storage.getModel()); taskCompleter.ready(dbClient); log.info("Restore Snapshot volume completed"); } catch (Exception e) { String message = String.format("Generic exception when trying to restore from snapshot %s on array %s", snapshot.toString(), storage.getSerialNumber()); log.error(message, e); ServiceError error = DeviceControllerErrors.hds.methodFailed("restoreSingleVolumeSnapshot", e.getMessage()); taskCompleter.error(dbClient, error); } } /** * Get replicationInfo object for the given pvol and svol from SnapshotGroup instance. */ private ReplicationInfo getReplicationInfo(SnapshotGroup snapShotGrp, String pvoldevnum, String svoldevnum) { ReplicationInfo repInfo = null; if (snapShotGrp != null && snapShotGrp.getReplicationInfoList() != null) { log.info("rep list size :{}", snapShotGrp.getReplicationInfoList().size()); for (ReplicationInfo replicationInfo : snapShotGrp.getReplicationInfoList()) { if (null != replicationInfo) { log.debug("Rep Info :{}", replicationInfo.toXMLString()); if (pvoldevnum.equals(replicationInfo.getPvolDevNum()) && svoldevnum.equals(replicationInfo.getSvolDevNum())) { log.info("Found replication info object :{}", replicationInfo.getObjectID()); repInfo = replicationInfo; break; } } } } return repInfo; } @Override public void establishVolumeSnapshotGroupRelation(StorageSystem storage, URI sourceVolume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } }