/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_INSTANCE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_REPLICATION_GROUP; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CREATE_GROUP; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CREATE_NEW_TARGET_VALUE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.DEFAULT_INSTANCE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.DELETE_GROUP; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.DESIRED_COPY_METHODOLOGY; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.EMC_RETURN_TO_STORAGE_POOL; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.GET_DEFAULT_REPLICATION_SETTING_DATA; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.MIRROR_REPLICATION_TYPE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SNAPSHOT_REPLICATION_TYPE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYMM_VIRTUAL_PROVISIONING_POOL; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.TARGET_ELEMENT_SUPPLIER; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.VP_SNAP_VALUE; import static java.text.MessageFormat.format; import static javax.cim.CIMDataType.UINT16_T; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; 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 javax.cim.UnsignedInteger16; import javax.wbem.CloseableIterator; import javax.wbem.WBEMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.BlockConsistencyGroup; 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.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYNC_TYPE; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateVmaxCGTargetVolumesJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisDeleteVmaxCGTargetVolumesJob; import com.emc.storageos.volumecontroller.impl.utils.ConsistencyGroupUtils; import com.google.common.base.Joiner; /** * Class to contain common utilities for Replication related operations */ public class ReplicationUtils { private static final Logger _log = LoggerFactory.getLogger(ReplicationUtils.class); public static class ReplicationSettingBuilder { private final StorageSystem storage; private final SmisCommandHelper helper; private final CIMObjectPathFactory cimPath; private final int replicationType; private final Set<CIMProperty> properties = new HashSet<>(); public ReplicationSettingBuilder(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath) { this(storage, helper, cimPath, SNAPSHOT_REPLICATION_TYPE); } public ReplicationSettingBuilder(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath, int replicationType) { this.storage = storage; this.helper = helper; this.cimPath = cimPath; this.replicationType = replicationType; } public CIMInstance build() throws WBEMException { CIMObjectPath repCapabilities = cimPath.getReplicationServiceCapabilitiesPath(storage); CIMArgument[] repSettingInArgs = helper.getReplicationSettingDataInstance(replicationType); CIMArgument[] repSettingOutArgs = new CIMArgument[5]; helper.invokeMethod(storage, repCapabilities, GET_DEFAULT_REPLICATION_SETTING_DATA, repSettingInArgs, repSettingOutArgs); CIMInstance modifiedInstance = (CIMInstance) cimPath.getFromOutputArgs(repSettingOutArgs, DEFAULT_INSTANCE); return modifiedInstance.deriveInstance(properties.toArray(new CIMProperty[] {})); } public ReplicationSettingBuilder addVPSnap() { return addProperty(new CIMProperty<Object>(DESIRED_COPY_METHODOLOGY, UINT16_T, new UnsignedInteger16(VP_SNAP_VALUE))); } public ReplicationSettingBuilder addCreateNewTarget() { return addProperty(new CIMProperty<Object>(TARGET_ELEMENT_SUPPLIER, UINT16_T, new UnsignedInteger16(CREATE_NEW_TARGET_VALUE))); } public ReplicationSettingBuilder addCopyBeforeActivate() { return addProperty(new CIMProperty<Object>(SmisConstants.DESIRED_COPY_METHODOLOGY, UINT16_T, new UnsignedInteger16(SmisConstants.COPY_BEFORE_ACTIVATE))); } public ReplicationSettingBuilder addDifferentialClone() { return addProperty(new CIMProperty<Object>(SmisConstants.DESIRED_COPY_METHODOLOGY, UINT16_T, new UnsignedInteger16(SmisConstants.DIFFERENTIAL_CLONE_VALUE))); } public ReplicationSettingBuilder addSMIS81TFDifferentialClone() { return addProperty(new CIMProperty<Object>(SmisConstants.DESIRED_COPY_METHODOLOGY, UINT16_T, new UnsignedInteger16(SmisConstants.SMIS810_TF_DIFFERENTIAL_CLONE_VALUE))); } public ReplicationSettingBuilder addConsistentPointInTime() { return addProperty(new CIMProperty<Object>(SmisConstants.CP_CONSISTENT_POINT_IN_TIME, CIMDataType.BOOLEAN_T, true)); } private ReplicationSettingBuilder addProperty(CIMProperty property) { properties.add(property); return this; } } /** * This method will conditionally invoke the SmisCommandHelper#callRefreshSystem * (storage) method to update the SMI-S database. The routine will check if any of * the passed in BlockObjects referenced by the URI list have their * emcRefreshRequired flag set. If so, the call will be made. * * @param dbClient - DbClient for accessing the ViPR db * @param helper - SmisCommandHelper reference * @param storage - StorageSystem object that this refresh would be called against * @param blockObjectURIs - list of BlockObject URIs to check */ public static void callEMCRefreshIfRequired(DbClient dbClient, SmisCommandHelper helper, StorageSystem storage, List<URI> blockObjectURIs) { try { if (blockObjectURIs != null) { List<URI> blockObjectsRequiringRefresh = new ArrayList<URI>(); boolean refreshIsRequired = false; for (URI uri : blockObjectURIs) { BlockObject object = BlockObject.fetch(dbClient, uri); if (object.getRefreshRequired()) { blockObjectsRequiringRefresh.add(uri); refreshIsRequired = true; } } if (refreshIsRequired) { SimpleFunction toUpdateRefreshRequired = new RefreshRequiredUpdateFunction(storage.getId(), blockObjectsRequiringRefresh, dbClient); _log.info(String.format("Following objects require EMCRefresh, " + "will attempt call:\n%s", Joiner.on(',').join(blockObjectsRequiringRefresh))); helper.callRefreshSystem(storage, toUpdateRefreshRequired); } else { _log.info("No EMCRefresh is required"); } } } catch (Exception e) { _log.error("Exception callEMCRefreshIfRequired", e); } } /** * Refresh the given storagesystem. * * @param helper * @param storage */ public static void callEMCRefresh(SmisCommandHelper helper, StorageSystem storage) { try { _log.info("Refreshing storagesystem: {}", storage.getId()); helper.callRefreshSystem(storage, null); } catch (Exception e) { _log.error("Exception callEMCRefresh", e); } } /** * Refresh the given storagesystem. * * @param helper reference to SmisCommandHelper * @param storage reference to StorageSystem * @param force flag to run refresh or not if threshold is not met */ public static void callEMCRefresh(SmisCommandHelper helper, StorageSystem storage, boolean force) { try { _log.info("Refreshing storagesystem: {}", storage.getId()); helper.callRefreshSystem(storage, null, force); } catch (Exception e) { _log.error("Exception callEMCRefresh", e); } } /** * Gets the default ReplicationSettingData object from the system and updates * the ConsistentPointInTime property to true. * * @param storage * @param thinProvisioning * @return CIMInstance - the instance of ReplicaSettingData * @throws WBEMException */ public static CIMInstance getReplicationSettingForGroupSnapshots(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath, boolean thinProvisioning) throws WBEMException { ReplicationSettingBuilder builder = new ReplicationSettingBuilder(storage, helper, cimPath); builder.addConsistentPointInTime(); // For 4.6 providers and VMAX3 arrays, we are creating target devices and target group before // calling 'CreateGroupReplica'. We need to create new target devices while // creating group replica only for 8.0 provider. if (storage.getUsingSmis80() && !storage.checkIfVmax3()) { builder.addCreateNewTarget(); } if (thinProvisioning) { // this should only apply to VMAX2 builder.addVPSnap(); } return builder.build(); } /** * Gets the default ReplicationSettingData object from the system and updates * the ConsistentPointInTime property to true in addition to clone-specific settings. * * @param storage * @return CIMInstance - the instance of ReplicaSettingData * @throws WBEMException */ public static CIMInstance getReplicationSettingForGroupClones(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath, boolean createInactive) throws WBEMException { ReplicationSettingBuilder builder = new ReplicationSettingBuilder(storage, helper, cimPath); builder.addConsistentPointInTime(); if (createInactive) { builder.addCopyBeforeActivate(); } else { builder.addDifferentialClone(); } return builder.build(); } /** * Gets the default ReplicationSettingData object from the system and updates * the ConsistentPointInTime property to true in addition to clone-specific settings. * * @param storage * @return CIMInstance - the instance of ReplicaSettingData * @throws WBEMException */ public static CIMInstance getReplicationSettingForSMIS81TFGroupClones(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath, boolean createInactive) throws WBEMException { ReplicationSettingBuilder builder = new ReplicationSettingBuilder(storage, helper, cimPath); builder.addConsistentPointInTime(); if (createInactive) { builder.addCopyBeforeActivate(); } else { builder.addSMIS81TFDifferentialClone(); } return builder.build(); } /** * Gets the default ReplicationSettingData object from the system and updates * the ConsistentPointInTime property to true. * * @param storage * @param helper * @param cimPath * @return CIMInstance - the instance of ReplicaSettingData * @throws WBEMException */ public static CIMInstance getReplicationSettingForGroupMirrors(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath) throws WBEMException { ReplicationSettingBuilder builder = new ReplicationSettingBuilder(storage, helper, cimPath, MIRROR_REPLICATION_TYPE); builder.addConsistentPointInTime(); return builder.build(); } /** * Enables VPSnaps by modifying the default ReplicationSettingData instance. * * @param storage The StorageSystem. * @return A modified ReplicationSettingData instance. * @throws WBEMException */ public static CIMInstance getVPSnapReplicationSetting(StorageSystem storage, SmisCommandHelper helper, CIMObjectPathFactory cimPath) throws WBEMException { ReplicationSettingBuilder builder = new ReplicationSettingBuilder(storage, helper, cimPath); return builder.addVPSnap().addCreateNewTarget().build(); } /** * Gets the target storage pool for VPSnap creation. * * All VPSnap targets created for a volume should belong to a single pool. * Hence, specify a target pool during snap creation. This is applicable for VMAX2 Thin volumes. * * Target pool id to be specified in following cases: * 1. first time snapshot creation for a volume or to a group of volumes. * 2. while scaling CG by adding new volume, * 3. subsequent snapshot creations. * * @param storage the storage * @param sourceGroupName the source group name * @param targetGroupName the target group name * @param dbClient the db client * @param helper the helper * @param cimPath the cim path * @return the target pool for vp snap creation */ public static CIMObjectPath getTargetPoolForVPSnapCreation(StorageSystem storage, String sourceGroupName, String targetGroupName, boolean thinProvisioning, DbClient dbClient, SmisCommandHelper helper, CIMObjectPathFactory cimPath) { _log.info("Get target storage pool for VPSnap creation. Source group name {}, target group name {}", sourceGroupName, targetGroupName); /** * If existing snapshot group is provided, query the SMI-S Provider to get the snapshot's storage pool information (case-2). * Else if source group is provided: * -if snapshot exists, get storage pool of snapshot (case-3). * -else take the storage pool of one of the source volume (case-1). */ BlockSnapshot existingTarget = null; Volume existingVolume = null; CIMObjectPath targetpoolPath = null; CloseableIterator<CIMObjectPath> poolPathItr = null; try { if (Type.vmax.toString().equals(storage.getSystemType()) && !storage.checkIfVmax3() && thinProvisioning) { if (targetGroupName != null) { existingTarget = getExistingTargetForTargetReplicationGroup(storage, targetGroupName, dbClient); } else if (sourceGroupName != null) { List<Volume> rgVolumes = ControllerUtils.getVolumesPartOfRG(storage.getId(), sourceGroupName, dbClient); Set<String> targetGroupNames = ControllerUtils.getSnapshotReplicationGroupNames(rgVolumes, dbClient); if (!targetGroupNames.isEmpty()) { targetGroupName = targetGroupNames.iterator().next(); _log.info("Taking target pool from one of existing snapshot group {}", targetGroupName); existingTarget = getExistingTargetForTargetReplicationGroup(storage, targetGroupName, dbClient); } else if (!rgVolumes.isEmpty()) { existingVolume = rgVolumes.get(0); } } if (existingTarget != null) { CIMObjectPath targetDevicePath = cimPath.getBlockObjectPath(storage, existingTarget); poolPathItr = helper.getAssociatorNames(storage, targetDevicePath, null, SYMM_VIRTUAL_PROVISIONING_POOL, null, null); // verify that a storage pool exists in DB for this snapshot's pool if (poolPathItr != null && poolPathItr.hasNext()) { CIMObjectPath poolPath = poolPathItr.next(); _log.debug("storage pool path {}", poolPath); String nativeGuid = NativeGUIDGenerator.generateNativeGuidForPool(poolPath); List<StoragePool> poolInDB = CustomQueryUtility.getActiveStoragePoolByNativeGuid(dbClient, nativeGuid); if (poolInDB != null && !poolInDB.isEmpty()) { StoragePool storagePool = poolInDB.get(0); _log.info("Storage Pool for the snapshot's pool exists in database. Snapshot: {}, storage pool: {}", existingTarget.getNativeGuid(), storagePool.getNativeGuid()); targetpoolPath = helper.getPoolPath(storage, storagePool); } } } else if (existingVolume != null) { StoragePool storagePool = dbClient.queryObject(StoragePool.class, existingVolume.getPool()); targetpoolPath = helper.getPoolPath(storage, storagePool); } else { _log.warn("Neither existing snapshot nor volume found to get target storage pool"); } } } catch (Exception ex) { _log.error("Exception while trying to get target storage pool path for VPSnap creation", ex); } finally { if (poolPathItr != null) { poolPathItr.close(); } } return targetpoolPath; } /** * Gets the existing target for target replication group. * * @param storage the storage * @param targetGroupName the target group name * @param dbClient the db client * @return the existing target for target replication group */ private static BlockSnapshot getExistingTargetForTargetReplicationGroup(StorageSystem storage, String targetGroupName, DbClient dbClient) { List<BlockSnapshot> existingSnapshots = ControllerUtils. getSnapshotsPartOfReplicationGroup(targetGroupName, storage.getId(), dbClient); Iterator<BlockSnapshot> itr = existingSnapshots.iterator(); while (itr.hasNext()) { BlockSnapshot snapshot = itr.next(); // skip the new snapshot to be created if (snapshot != null && snapshot.getNativeId() != null) { _log.info("considering snapshot {}", snapshot.getLabel()); return snapshot; } } return null; } /** * Checks that the replication group is accessible from this storage system, using its currently active * storage provider. * * @param storage StorageSystem * @param replica BlockObject * @throws com.emc.storageos.exceptions.DeviceControllerException When the replication group isn't found. */ public static void checkReplicationGroupAccessibleOrFail(StorageSystem storage, BlockObject replica, DbClient dbClient, SmisCommandHelper helper, CIMObjectPathFactory cimPath) throws Exception { BlockConsistencyGroup blockConsistencyGroup = dbClient.queryObject( BlockConsistencyGroup.class, replica.getConsistencyGroup()); String deviceName = ConsistencyGroupUtils.getSourceConsistencyGroupName(replica, dbClient); String label = blockConsistencyGroup.getLabel(); CIMObjectPath path = cimPath.getReplicationGroupPath(storage, deviceName); CIMInstance instance = helper.checkExists(storage, path, false, false); if (instance == null) { String msg = String.format("ReplicationGroup %s was not found on provider %s. " + "It may have already been deleted, or check SMI-S providers for connection issues or failover.", deviceName, storage.getActiveProviderURI()); _log.warn(msg); throw DeviceControllerException.exceptions.consistencyGroupNotFoundForProvider(deviceName, label, storage.getSmisProviderIP()); } } public static CIMObjectPath getCloneGroupSynchronizedPath(StorageSystem storage, URI cloneUri, DbClient dbClient, SmisCommandHelper helper, CIMObjectPathFactory cimPath) { Volume clone = dbClient.queryObject(Volume.class, cloneUri); Volume sourceVol = dbClient.queryObject(Volume.class, clone.getAssociatedSourceVolume()); String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sourceVol, dbClient); String replicationGroupName = clone.getReplicationGroupInstance(); return cimPath.getGroupSynchronizedPath(storage, consistencyGroupName, replicationGroupName); } /** * Deletes a target group represented by the given target group path * * @param storage - StorageSystem where the target group is * @param targetGroupPath - Path representing target group to be deleted * * @throws DeviceControllerException */ public static void deleteTargetDeviceGroup(final StorageSystem storage, final CIMObjectPath targetGroupPath, final DbClient dbClient, final SmisCommandHelper helper, final CIMObjectPathFactory cimPath) { _log.info(format("Removing target device group {0} from storage system {1}", targetGroupPath.toString(), storage.getId())); try { CIMObjectPath replicationSvc = cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] outArgs = new CIMArgument[5]; CIMArgument[] inArgs = helper.getDeleteReplicationGroupInputArguments(storage, targetGroupPath, true); helper.invokeMethod(storage, replicationSvc, DELETE_GROUP, inArgs, outArgs); } catch (Exception e) { _log.error( format("An error occurred when removing target device group {0} from storage system {1}", targetGroupPath, storage.getId()), e); } } /** * Method will invoke the SMI-S operation to create the target volumes * * @param storageSystem - StorageSystem where the pool and snapshot exist * @param sourceGroupName - Name of source group * @param label - Name to be applied to each snapshot volume * @param createInactive - whether the snapshot needs to to be created with sync_active=true/false * @param count - Number of target Volumes to create * @param storagePoolUri - Storage Pool to use for creation of target volumes. * @param capacity - Size of the Volumes to create * @param isThinlyProvisioned * @param taskCompleter - Completer object used for task status update * @param dbClient * @param helper - smisCommandHelper * @param cimPath - CIMObjectPathFactory * * @throws DeviceControllerException * * @returns - List of native Ids */ public static List<String> createTargetDevices(StorageSystem storageSystem, String sourceGroupName, String label, Boolean createInactive, int count, URI storagePoolUri, long capacity, boolean isThinlyProvisioned, Volume sourceVolume, TaskCompleter taskCompleter, DbClient dbClient, SmisCommandHelper helper, CIMObjectPathFactory cimPath) throws DeviceControllerException { _log.info(format("Creating target devices for: Storage System: {0}, Consistency Group: {1}, Pool: {2}, Count: {3}", storageSystem.getId(), sourceGroupName, storagePoolUri, count)); try { StoragePool storagePool = dbClient.queryObject(StoragePool.class, storagePoolUri); CIMObjectPath configSvcPath = cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs = null; if (storageSystem.checkIfVmax3()) { CIMObjectPath volumeGroupPath = helper.getVolumeGroupPath(storageSystem, storageSystem, sourceVolume, storagePool); CIMObjectPath poolPath = helper.getPoolPath(storageSystem, storagePool); inArgs = helper.getCreateVolumesBasedOnVolumeGroupInputArguments(storageSystem, poolPath, volumeGroupPath, label, count, capacity); } else { inArgs = helper.getCreateVolumesInputArguments(storageSystem, storagePool, label, capacity, count, isThinlyProvisioned, null, true); } CIMArgument[] outArgs = new CIMArgument[5]; SmisCreateVmaxCGTargetVolumesJob job = new SmisCreateVmaxCGTargetVolumesJob(null, storageSystem.getId(), sourceGroupName, label, createInactive, taskCompleter); helper.invokeMethodSynchronously(storageSystem, configSvcPath, helper.createVolumesMethodName(storageSystem), inArgs, outArgs, job); return job.getTargetDeviceIds(); } catch (Exception e) { final String errMsg = format("An error occurred when creating target devices VMAX system {0}", storageSystem.getId()); _log.error(errMsg, e); taskCompleter.error(dbClient, SmisException.errors.methodFailed(helper.createVolumesMethodName(storageSystem), e.getMessage())); throw new SmisException(errMsg, e); } } /** * Creates a target group that will contain the devices in 'deviceIds'. * * @param storage - StorageSystem where target device will be created * @param sourceGroupName - The name of the source volumes group * @param deviceIds - Device native IDs of the target VDEVs * @param taskCompleter - Completer object used for task status update * @return CIMObjectPath - null => Error. Otherwise, it represents the * TargetDeviceGroup object created * * @throws DeviceControllerException */ public static CIMObjectPath createTargetDeviceGroup(StorageSystem storage, String sourceGroupName, List<String> deviceIds, TaskCompleter taskCompleter, DbClient dbClient, SmisCommandHelper helper, CIMObjectPathFactory cimPath, SYNC_TYPE syncType) throws DeviceControllerException { try { CIMObjectPath replicationSvc = cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] outArgs = new CIMArgument[5]; CIMObjectPath[] volumePaths = cimPath.getVolumePaths(storage, deviceIds.toArray(new String[deviceIds.size()])); CIMArgument[] inArgs = null; if (syncType == SYNC_TYPE.SNAPSHOT) { inArgs = helper.getCreateReplicationGroupCreateInputArguments(storage, null, volumePaths); } else { inArgs = helper.getCreateReplicationGroupWithMembersInputArguments(storage, null, volumePaths); } helper.invokeMethod(storage, replicationSvc, CREATE_GROUP, inArgs, outArgs); CIMObjectPath path = cimPath.getCimObjectPathFromOutputArgs(outArgs, CP_REPLICATION_GROUP); return path; } catch (Exception e) { taskCompleter.error(dbClient, SmisException.errors.methodFailed(CREATE_GROUP, e.getMessage())); throw new SmisException("Error when creating target device group", e); } } public static void rollbackCreateReplica(final StorageSystem storage, final CIMObjectPath targetGroupPath, final List<String> targetDeviceIds, final TaskCompleter taskCompleter, final DbClient dbClient, final SmisCommandHelper helper, final CIMObjectPathFactory cimPath) throws DeviceControllerException { _log.info(format("Rolling back snapshot creation on storage system {0}", storage.getId())); try { // Remove target group if (targetGroupPath != null) { deleteTargetDeviceGroup(storage, targetGroupPath, dbClient, helper, cimPath); } // Remove target devices if (targetDeviceIds != null && !targetDeviceIds.isEmpty()) { deleteTargetDevices(storage, targetDeviceIds.toArray(new String[targetDeviceIds.size()]), taskCompleter, dbClient, helper, cimPath); } } catch (DeviceControllerException e) { final String errMsg = format("Unable to rollback snapshot creation on storage system {0}", storage.getId()); _log.error(errMsg, e); throw new SmisException(errMsg, e); } } /** * Method will invoke the SMI-S operation to return the Volumes represented by the native ids to the storage pool * * @param storageSystem - StorageSystem where the pool and volume exist * @param deviceIds - List of native Ids representing the elements to be returned to the pool * @param taskCompleter - Completer object used for task status update * * @throws DeviceControllerException */ public static void deleteTargetDevices(final StorageSystem storageSystem, final String[] deviceIds, final TaskCompleter taskCompleter, final DbClient dbClient, final SmisCommandHelper helper, final CIMObjectPathFactory cimPath) { _log.info(format("Removing target devices {0} from storage system {1}", Joiner.on(',').join(deviceIds), storageSystem.getId())); try { if (storageSystem.checkIfVmax3()) { for (String deviceId : deviceIds) { helper.removeVolumeFromParkingSLOStorageGroup(storageSystem, deviceId, false); _log.info("Done invoking remove volume {} from parking SLO storage group", deviceId); } } CIMArgument[] outArgs = new CIMArgument[5]; CIMArgument[] inArgs = null; String method = null; CIMObjectPath configSvcPath = cimPath.getConfigSvcPath(storageSystem); if (storageSystem.deviceIsType(Type.vmax)) { final CIMObjectPath[] theElements = cimPath.getVolumePaths(storageSystem, deviceIds); inArgs = helper.getReturnElementsToStoragePoolArguments(theElements, SmisConstants.CONTINUE_ON_NONEXISTENT_ELEMENT); method = RETURN_ELEMENTS_TO_STORAGE_POOL; } else { inArgs = helper.getDeleteVolumesInputArguments(storageSystem, deviceIds); method = EMC_RETURN_TO_STORAGE_POOL; } final SmisDeleteVmaxCGTargetVolumesJob job = new SmisDeleteVmaxCGTargetVolumesJob( null, storageSystem.getId(), deviceIds, taskCompleter); helper.invokeMethodSynchronously(storageSystem, configSvcPath, method, inArgs, outArgs, job); } catch (Exception e) { _log.error( format("An error occurred when removing target devices {0} from storage system {1}", deviceIds, storageSystem.getId()), e); } } public static CIMObjectPath getMirrorGroupSynchronizedPath(StorageSystem storage, URI mirrorUri, DbClient dbClient, SmisCommandHelper helper, CIMObjectPathFactory cimPath) { BlockMirror mirror = dbClient.queryObject(BlockMirror.class, mirrorUri); Volume sourceVol = dbClient.queryObject(Volume.class, mirror.getSource()); String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sourceVol, dbClient); String replicationGroupName = mirror.getReplicationGroupInstance(); return cimPath.getGroupSynchronizedPath(storage, consistencyGroupName, replicationGroupName); } /** * Deletes a replication group * * @param storage StorageSystem * @param groupName replication group to be deleted * @param dbClient * @param helper * @param cimPath * * @throws DeviceControllerException */ public static void deleteReplicationGroup(final StorageSystem storage, final String groupName, final DbClient dbClient, final SmisCommandHelper helper, final CIMObjectPathFactory cimPath) { try { CIMObjectPath cgPath = cimPath.getReplicationGroupPath(storage, groupName); CIMObjectPath replicationSvc = cimPath.getControllerReplicationSvcPath(storage); CIMInstance cgPathInstance = helper.checkExists(storage, cgPath, false, false); if (cgPathInstance != null) { // Invoke the deletion of the consistency group CIMArgument[] inArgs = helper.getDeleteReplicationGroupInputArguments(storage, groupName); helper.invokeMethod(storage, replicationSvc, SmisConstants.DELETE_GROUP, inArgs, new CIMArgument[5]); } } catch (Exception e) { _log.error("Failed to delete replication group: ", e); } } /** * Utility function to remove a full copy that is being detached from its source * from the list full copies for the source volume. * * @param fullCopy A reference to a full copy being detached from its source. * @param dbClient A reference to a database client. */ public static void removeDetachedFullCopyFromSourceFullCopiesList(Volume fullCopy, DbClient dbClient) { URI sourceURI = fullCopy.getAssociatedSourceVolume(); if ((!NullColumnValueGetter.isNullURI(sourceURI)) && (URIUtil.isType(sourceURI, Volume.class))) { Volume sourceVolume = dbClient.queryObject(Volume.class, sourceURI); StringSet fullCopies = sourceVolume.getFullCopies(); String fullCopyId = fullCopy.getId().toString(); if ((fullCopies != null) && (fullCopies.contains(fullCopyId))) { fullCopies.remove(fullCopyId); dbClient.persistObject(sourceVolume); } } } }