/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.vmax; import static com.emc.storageos.db.client.constraint.ContainmentConstraint.Factory.getLinkedTargetSnapshotSessionConstraint; import static com.emc.storageos.db.client.util.CommonTransformerFunctions.fctnBlockObjectToNativeID; import static com.emc.storageos.db.client.util.CustomQueryUtility.queryActiveResourcesByConstraint; import static com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils.callEMCRefresh; import static com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils.callEMCRefreshIfRequired; import static com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils.checkReplicationGroupAccessibleOrFail; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.COPY_STATE_MIXED_INT_VALUE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.COPY_STATE_RESTORED_INT_VALUE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_EMC_UNIQUE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_INSTANCE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_STORAGE_EXTENT_INITIAL_USAGE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CREATE_SETTING; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.DELETE_GROUP; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.DELTA_REPLICA_TARGET_VALUE; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.NEW_SETTING; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.PS_COPY_STATE_AND_DESC; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SE_GROUP_SYNCHRONIZED_RG_RG; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SE_REPLICATION_GROUP; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYMM_SNAP_STORAGE_POOL; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYMM_STORAGE_POOL_CAPABILITIES; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYMM_STORAGE_POOL_SETTING; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.Collections2.filter; import static com.google.common.collect.Collections2.transform; import static com.google.common.collect.Lists.newArrayList; import static java.text.MessageFormat.format; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.cim.CIMArgument; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; 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.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.NamedElementQueryResultList; 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.Operation; import com.emc.storageos.db.client.model.StoragePool; 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.model.util.BlockConsistencyGroupUtils; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.exceptions.DeviceControllerExceptions; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.job.QueueJob; import com.emc.storageos.volumecontroller.impl.providerfinders.FindProviderFactory; import com.emc.storageos.volumecontroller.impl.smis.AbstractSnapshotOperations; import com.emc.storageos.volumecontroller.impl.smis.CIMPropertyFactory; import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYNC_TYPE; import com.emc.storageos.volumecontroller.impl.smis.SmisException; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockCreateCGSnapshotJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockCreateSnapshotJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockRestoreSnapshotJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockResumeSnapshotJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockResyncSnapshotJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionCGCreateJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionCreateJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionDeleteJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionLinkTargetGroupJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionLinkTargetJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionRelinkTargetJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionRestoreJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockSnapshotSessionUnlinkTargetJob; 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.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class VmaxSnapshotOperations extends AbstractSnapshotOperations { private static final Logger _log = LoggerFactory.getLogger(VmaxSnapshotOperations.class); private static final String[] PL_ONLY_EMC_UNIQUE_ID = new String[] { CP_EMC_UNIQUE_ID }; private static final String[] PL_STORAGE_EXTENT_INITIAL_USAGE = new String[] { CP_STORAGE_EXTENT_INITIAL_USAGE }; private FindProviderFactory findProviderFactory; public void setFindProviderFactory(final FindProviderFactory findProviderFactory) { this.findProviderFactory = findProviderFactory; } /** * This interface is for the snapshot active. The createSnapshot may have done * whatever is necessary to setup the snapshot for this call. The goal is to * make this a quick operation and the create operation has already done a lot * of the "heavy lifting". * * @param storage [required] - StorageSystem object representing the array * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void activateSingleVolumeSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); if (snapshotObj.getIsSyncActive()) { _log.warn("Trying to activate snapshot, which is already active", snapshotObj.getId().toString()); return; } _log.info("activateSingleVolumeSnapshot operation START"); CIMArgument[] inArgs = _helper.getActivateSnapshotInputArguments(storage, snapshotObj); CIMArgument[] outArgs = new CIMArgument[5]; _helper.callModifyReplica(storage, inArgs, outArgs); setIsSyncActive(snapshotObj, true); snapshotObj.setRefreshRequired(true); _dbClient.persistObject(snapshotObj); // Success -- Update status taskCompleter.ready(_dbClient); } catch (Exception e) { _log.info("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, error); } finally { _log.info("activateSingleVolumeSnapshot operation END"); } } /** * This interface is for the snapshot active. The createSnapshot may have done * whatever is necessary to setup the snapshot for this call. The goal is to * make this a quick operation and the create operation has already done a lot * of the "heavy lifting". * * @param storage [required] - StorageSystem object representing the array * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void activateGroupSnapshots(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { _log.info("activateGroupSnapshots operation START"); BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); if (snapshotObj.getIsSyncActive()) { _log.warn("Trying to activate CG snapshot, which is already active", snapshotObj.getId().toString()); return; } // Check if the consistency group exists String groupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, _dbClient); storage = findProviderFactory.withGroup(storage, groupName).find(); if (storage == null) { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } Volume sourceVolume = _dbClient.queryObject(Volume.class, snapshotObj.getParent().getURI()); boolean isSuccess = VmaxGroupOperationsUtils.activateGroupReplicas(storage, sourceVolume, snapshotObj, SYNC_TYPE.SNAPSHOT, taskCompleter, _dbClient, _helper, _cimPath); if (isSuccess) { List<BlockSnapshot> snapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup( snapshotObj, _dbClient); setIsSyncActive(snapshots, true); for (BlockSnapshot it : snapshots) { it.setRefreshRequired(true); } _dbClient.persistObject(snapshots); taskCompleter.ready(_dbClient); } } catch (Exception e) { _log.info("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, error); } finally { _log.info("activateGroupSnapshots operation END"); } } /** * Should implement deletion of single volume snapshot. That is, deleting a snap that was * created independent of other volumes. * * @param storage [required] - StorageSystem object representing the array * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void deleteSingleVolumeSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("START deleteSingleVolumeSnapshot"); try { callEMCRefreshIfRequired(_dbClient, _helper, storage, Arrays.asList(snapshot)); BlockSnapshot snap = _dbClient.queryObject(BlockSnapshot.class, snapshot); CIMObjectPath syncObjectPath = _cimPath.getSyncObject(storage, snap); if (_helper.checkExists(storage, syncObjectPath, false, false) != null) { deactivateSnapshot(storage, snap, syncObjectPath); if (storage.checkIfVmax3()) { // Ingested non-exported snapshot could be associated with SGs outside of ViPR, // remove snapshot from them before deleting it. _helper.removeVolumeFromStorageGroupsIfVolumeIsNotInAnyMV(storage, snap); _helper.removeVolumeFromParkingSLOStorageGroup(storage, snap.getNativeId(), false); _log.info("Done invoking remove volume {} from parking SLO storage group", snap.getNativeId()); // If VMAX3 linked target (both copy and no copy mode), detach the element synchronization before deleting it. // COP-21476 - 'no copy' mode target too needs to be detached when snap session has linked copy mode target. // TODO enhance ReplicaDeviceController to handle remove single session & target while removing volume from group. CIMArgument[] inArgsDetach = _helper.getUnlinkBlockSnapshotSessionTargetInputArguments(syncObjectPath); CIMArgument[] outArgsDetach = new CIMArgument[5]; CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(storage); _helper.invokeMethodSynchronously(storage, replicationSvcPath, SmisConstants.MODIFY_REPLICA_SYNCHRONIZATION, inArgsDetach, outArgsDetach, null); // delete the target snapshot device CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storage); CIMArgument[] inArgs = _helper.getDeleteVolumesInputArguments(storage, new String[] { snap.getNativeId() }); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethodSynchronously(storage, configSvcPath, SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL, inArgs, outArgs, null); } else { CIMArgument[] outArgs = new CIMArgument[5]; _helper.callModifyReplica(storage, _helper.getDeleteSnapshotSynchronousInputArguments(syncObjectPath), outArgs); } snap.setInactive(true); snap.setIsSyncActive(false); _dbClient.updateObject(snap); taskCompleter.ready(_dbClient); } else { // Perhaps, it's already been deleted or was deleted on the array. // In that case, we'll just say all is well, so that this operation // is idempotent. snap.setInactive(true); snap.setIsSyncActive(false); _dbClient.updateObject(snap); taskCompleter.ready(_dbClient); } } catch (WBEMException e) { String message = String.format("Error encountered during delete snapshot %s on array %s", snapshot.toString(), storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, error); } catch (Exception e) { String message = String.format("Generic exception when trying to delete snapshot %s on array %s", snapshot.toString(), storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("deleteSingleVolumeSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } /** * Should implement creation of a single volume snapshot. That is a volume that * is not in any consistency group. * * @param storage [required] - StorageSystem object representing the array * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param createInactive - whether the snapshot needs to to be created with sync_active=true/false * @param readOnly - Indicates if the snapshot should be read only. * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void createSingleVolumeSnapshot(StorageSystem storage, URI snapshot, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException { // List of target device ids List<String> targetDeviceIds = null; try { BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); _log.info("createSingleVolumeSnapshot operation START"); Volume volume = _dbClient.queryObject(Volume.class, snapshotObj.getParent()); // Need to terminate an restore sessions, so that we can // restore from the same snapshot multiple times terminateAnyRestoreSessionsForVolume(storage, volume, taskCompleter); TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, volume.getTenant().getURI()); String tenantName = tenant.getLabel(); String snapLabelToUse = _nameGenerator.generate(tenantName, snapshotObj.getLabel(), snapshot.toString(), '-', storage.getUsingSmis80() ? SmisConstants.MAX_SMI80_SNAPSHOT_NAME_LENGTH : SmisConstants.MAX_SNAPSHOT_NAME_LENGTH); CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; if (storage.checkIfVmax3()) { CIMObjectPath volumeGroupPath = _helper.getVolumeGroupPath(storage, storage, volume, null); // COP-17240: For VMAX3, we will derive the target volumes from the source volumes SRP Pool CIMObjectPath poolPath = _helper.getVolumeStoragePoolPath(storage, volume); targetDeviceIds = createTargetDevices(storage, poolPath, volumeGroupPath, null, "SingleSnapshot", snapLabelToUse, createInactive, 1, volume.getCapacity(), taskCompleter); CIMInstance replicaSettingData = _helper.getReplicationSettingData(storage, targetDeviceIds.get(0), false); inArgs = _helper.getCreateElementReplicaSnapInputArgumentsWithTargetAndSetting(storage, volume, targetDeviceIds.get(0), replicaSettingData, createInactive, snapLabelToUse); } else { if (volume.getThinlyProvisioned()) { CIMInstance replicationSetting = ReplicationUtils.getVPSnapReplicationSetting(storage, _helper, _cimPath); inArgs = _helper.getCreateElementReplicaVPSnapInputArguments(storage, volume, createInactive, snapLabelToUse, replicationSetting); } else { inArgs = _helper.getCreateElementReplicaSnapInputArguments(storage, volume, createInactive, snapLabelToUse); } } _helper.invokeMethod(storage, replicationSvcPath, SmisConstants.CREATE_ELEMENT_REPLICA, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob( new QueueJob(new SmisBlockCreateSnapshotJob(job, storage.getId(), !createInactive, taskCompleter))); } } catch (Exception e) { _log.info("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, error); setInactive(snapshot, true); // Roll back changes if (storage.checkIfVmax3()) { rollbackCreateSnapshot(storage, null, targetDeviceIds, taskCompleter); } } } /** * Should implement create of a snapshot from a source volume that is part of a * consistency group. * * Implementation note: In this method we will kick of the asynchronous creation * of the target devices required for the CG snaps. Upon the successful * device creation, the post operations will take place, which will include the * creation of the target group and the group snapshot operation. * * @param storage [required] - StorageSystem object representing the array * @param snapshotList [required] - BlockSnapshot URI list representing the previously created * snap for the volume * @param createInactive whether the snapshot needs to to be created with sync_active=true/false * @param readOnly - Indicates if the snapshot should be read only. * @param taskCompleter - TaskCompleter object used for the updating operation status. * @throws DeviceControllerException */ @Override public void createGroupSnapshots(StorageSystem storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("START createGroupSnapshots"); // Target group CIM Path CIMObjectPath targetGroupPath = null; // List of target device ids List<String> targetDeviceIds = null; // The source consistency group name String sourceGroupName = null; try { final BlockSnapshot first = _dbClient.queryObject(BlockSnapshot.class, snapshotList.get(0)); sourceGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(first, _dbClient); Volume snapVolume = _dbClient.queryObject(Volume.class, first.getParent()); boolean thinProvisioning = snapVolume.getThinlyProvisioned() != null && snapVolume.getThinlyProvisioned(); TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, snapVolume.getTenant().getURI()); String tenantName = tenant.getLabel(); final String snapLabelToUse = _nameGenerator.generate(tenantName, first.getLabel(), snapshotList.get(0).toString(), '-', storage.getUsingSmis80() ? SmisConstants.MAX_SMI80_SNAPSHOT_NAME_LENGTH : SmisConstants.MAX_SNAPSHOT_NAME_LENGTH); // CTRL-5640: ReplicationGroup may not be accessible after provider fail-over. ReplicationUtils.checkReplicationGroupAccessibleOrFail(storage, first, _dbClient, _helper, _cimPath); final Map<String, List<Volume>> volumesBySizeMap = new HashMap<String, List<Volume>>(); // Group volumes by pool and size for (URI uri : snapshotList) { final BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, uri); final Volume volume = _dbClient.queryObject(Volume.class, snapshot.getParent().getURI()); final String key = volume.getPool() + "-" + volume.getCapacity(); final List<Volume> currentVolumes = volumesBySizeMap.containsKey(key) ? volumesBySizeMap.get(key) : new ArrayList<Volume>(); currentVolumes.add(volume); volumesBySizeMap.put(key, currentVolumes); } // For 8.0 providers (except VMAX3), no need to create target devices and // target group separately for volumes in CG. // They will be created as part of 'CreateGroupReplica' call. // For 4.6 providers, target devices and target group will be // created separately before 'CreateGroupReplica' call. // For VMAX3, we need the target group to tag the setting instance if (storage.checkIfVmax3() || !storage.getUsingSmis80()) { targetDeviceIds = new ArrayList<String>(); for (Entry<String, List<Volume>> entry : volumesBySizeMap.entrySet()) { final List<Volume> volumes = entry.getValue(); final Volume volume = volumes.get(0); final URI poolId = volume.getPool(); CIMObjectPath volumeGroupPath = _helper.getVolumeGroupPath(storage, storage, volume, null); // Create target devices based on the array model final List<String> newDeviceIds = kickOffTargetDevicesCreation(storage, volumeGroupPath, sourceGroupName, snapLabelToUse, createInactive, thinProvisioning, volumes.size(), poolId, volume.getCapacity(), taskCompleter); targetDeviceIds.addAll(newDeviceIds); } // Create target device group targetGroupPath = ReplicationUtils.createTargetDeviceGroup(storage, sourceGroupName, targetDeviceIds, taskCompleter, _dbClient, _helper, _cimPath, SYNC_TYPE.SNAPSHOT); } // Create CG snapshot CIMObjectPath job = VmaxGroupOperationsUtils.internalCreateGroupReplica(storage, sourceGroupName, snapLabelToUse, targetGroupPath, createInactive, thinProvisioning, taskCompleter, SYNC_TYPE.SNAPSHOT, _dbClient, _helper, _cimPath); if (job != null) { ControllerServiceImpl.enqueueJob( new QueueJob(new SmisBlockCreateCGSnapshotJob(job, storage.getId(), !createInactive, sourceGroupName, taskCompleter))); } } catch (Exception e) { final String errMsg = format( "An exception occurred when trying to create snapshots for consistency group {0} on storage system {1}", sourceGroupName, storage.getId()); _log.error(errMsg, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("createGroupSnapshots", e.getMessage()); taskCompleter.error(_dbClient, error); // Roll back changes rollbackCreateSnapshot(storage, targetGroupPath, targetDeviceIds, taskCompleter); throw new SmisException(errMsg, e); } } /** * Should implement clean up of all the snapshots in a volume consistency * group 'snap-set'. The 'snap-set' is a set of block snapshots created for a * set of volumes in a consistency group. * * @param storage [required] - StorageSystem object representing the array * @param snapshot [required] - BlockSnapshot object representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void deleteGroupSnapshots(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("START deleteGroupSnapshots"); try { callEMCRefreshIfRequired(_dbClient, _helper, storage, Arrays.asList(snapshot)); BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); // Check if the consistency group exists String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, _dbClient); StorageSystem newStorage = findProviderFactory.withGroup(storage, consistencyGroupName).find(); if (newStorage == null) { _log.warn("Replication Group {} not found.", consistencyGroupName); // Don't return, let the below code do its clean-up. } // else - storage will have right Provider to use. String snapshotGroupName = snapshotObj.getReplicationGroupInstance(); if (snapshotGroupName.contains("+")) { // quick fix for correcting 000198700406+EMC_SMI_RG1430326858108 snapshotGroupName in 803+VMAX if (storage.getUsingSmis80()) { snapshotGroupName = snapshotGroupName.substring(snapshotGroupName.indexOf("+") + 1); } else { snapshotGroupName = snapshotGroupName.substring(0, snapshotGroupName.indexOf("+")); } } List<BlockSnapshot> snapshotList = ControllerUtils.getSnapshotsPartOfReplicationGroup( snapshotObj, _dbClient); CIMArgument[] outArgs = new CIMArgument[5]; boolean deleteTarget = true; CIMObjectPath targetGroupPath = _cimPath.getReplicationGroupPath(storage, snapshotGroupName); if (targetGroupPath != null) { CIMObjectPath groupSynchronized = _cimPath.getGroupSynchronizedPath(storage, consistencyGroupName, snapshotGroupName); if (_helper.checkExists(storage, groupSynchronized, false, false) != null) { // remove targets from parking SLO group if (storage.checkIfVmax3()) { Iterator<BlockSnapshot> iter = snapshotList.iterator(); while (iter.hasNext()) { BlockSnapshot blockSnapshot = iter.next(); _helper.removeVolumeFromParkingSLOStorageGroup(storage, blockSnapshot.getNativeId(), false); _log.info("Done invoking remove volume {} from parking SLO storage group", blockSnapshot.getNativeId()); } // If VMAX3 linked target (both copy and no copy mode), detach the element synchronization before deleting it. // COP-21476 - 'no copy' mode target too needs to be detached when snap session has linked copy mode target. CIMArgument[] inArgsDetach = _helper.getUnlinkBlockSnapshotSessionTargetInputArguments(groupSynchronized); CIMArgument[] outArgsDetach = new CIMArgument[5]; CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(storage); _helper.invokeMethodSynchronously(storage, replicationSvcPath, SmisConstants.MODIFY_REPLICA_SYNCHRONIZATION, inArgsDetach, outArgsDetach, null); } else { /* * The VMAX2 MRS(ReturnToResourcePool) call internally does 3 operations: * 1) Detach GroupSynchronized * 2) Delete target storage group * 3) Delete target devices * * So, if the call succeeds we no longer need to perform delete target steps (set flag to false). */ CIMArgument[] deleteCGSnapInput = _helper.getDeleteSnapshotSynchronousInputArguments(groupSynchronized); _helper.callModifyReplica(storage, deleteCGSnapInput, outArgs); deleteTarget = false; } } else { _log.info("GroupSynchronized {} not found", groupSynchronized.toString()); } } if (deleteTarget) { /** * If ModifyReplicaSynchrnization for group synchronized fails, it may have failed at any stage. * -after detaching group synchronization, or after deleting target group * When user retries this operation, make required call based on what is needed. */ List<String> targetDeviceIds = new ArrayList<String>(); Iterator<BlockSnapshot> snapshotIter = snapshotList.iterator(); while (snapshotIter.hasNext()) { BlockSnapshot snap = snapshotIter.next(); if (!isNullOrEmpty(snap.getNativeId())) { targetDeviceIds.add(snap.getNativeId()); } } // Remove target group if (targetGroupPath != null) { ReplicationUtils.deleteTargetDeviceGroup(storage, targetGroupPath, _dbClient, _helper, _cimPath); } // Remove target devices if (!targetDeviceIds.isEmpty()) { ReplicationUtils.deleteTargetDevices(storage, targetDeviceIds.toArray(new String[targetDeviceIds.size()]), taskCompleter, _dbClient, _helper, _cimPath); } } // Set inactive=true for all snapshots in the snaps set Iterator<BlockSnapshot> snapshotIter = snapshotList.iterator(); while (snapshotIter.hasNext()) { BlockSnapshot it = snapshotIter.next(); it.setInactive(true); it.setIsSyncActive(false); _dbClient.updateObject(it); } taskCompleter.ready(_dbClient); } catch (Exception e) { String message = String.format("Generic exception when trying to delete snapshots from consistency group array %s", storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("deleteGroupSnapshots", e.getMessage()); taskCompleter.error(_dbClient, error); } } /** * Implementation for restoring of a single volume snapshot restore. That is, this * volume is independent of other volumes and a snapshot was taken previously, and * now we want to restore that snap to the original volume. * * @param storage [required] - StorageSystem object representing the array * @param volume [required] - Volume URI for the volume to be restored * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void restoreSingleVolumeSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { Volume vol = _dbClient.queryObject(Volume.class, volume); try { _helper.doApplyRecoverPointTag(storage, vol, false); callEMCRefreshIfRequired(_dbClient, _helper, storage, Arrays.asList(snapshot)); BlockSnapshot from = _dbClient.queryObject(BlockSnapshot.class, snapshot); CIMObjectPath syncObjectPath = _cimPath.getSyncObject(storage, from); if (_helper.checkExists(storage, syncObjectPath, false, false) != null) { terminateAnyRestoreSessions(storage, from, volume, taskCompleter); } CIMObjectPath cimJob; if (storage.checkIfVmax3()) { Volume to = _dbClient.queryObject(Volume.class, volume); cimJob = _helper.callModifySettingsDefineState(storage, _helper.getRestoreFromSnapshotInputArguments(storage, to, from)); } else { cimJob = _helper.callModifyReplica(storage, _helper.getRestoreFromReplicaInputArguments(syncObjectPath)); } ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockRestoreSnapshotJob(cimJob, storage.getId(), taskCompleter))); } catch (WBEMException e) { String message = String.format("Error encountered when trying to restore from snapshot %s on array %s", snapshot.toString(), storage.getSerialNumber()); _log.error(message, e); try { // Re-enable the RP tag. _log.info(String.format("Enabling the RecoverPoint tag on volume %s", volume.toString())); _helper.doApplyRecoverPointTag(storage, vol, true); } catch (Exception ex) { _log.error(String.format("An error has occured trying to enable the RecoverPoint tag on volume %s."), ex); } ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, error); } 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); try { // Re-enable the RP tag. _log.info(String.format("Enabling the RecoverPoint tag on volume %s", volume.toString())); _helper.doApplyRecoverPointTag(storage, vol, true); } catch (Exception ex) { _log.error(String.format("An error has occured trying to enable the RecoverPoint tag on volume %s."), ex); } ServiceError error = DeviceControllerErrors.smis.methodFailed("restoreSingleVolumeSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } /** * Implementation should restore the set of snapshots that were taken for a set of * volumes in a consistency group. That is, at some time there was a consistency * group of volumes created and snapshot was taken of these; these snapshots would * belong to a "snap-set". This restore operation, will restore the volumes in the * consistency group from this snap-set. Any snapshot from the snap-set can be * provided to restore the whole snap-set. * * @param storage [required] - StorageSystem object representing the array * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void restoreGroupSnapshots(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { callEMCRefreshIfRequired(_dbClient, _helper, storage, Arrays.asList(snapshot)); BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); // Check if the consistency group exists String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, _dbClient); storage = findProviderFactory.withGroup(storage, consistencyGroupName).find(); if (storage == null) { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } String snapshotGroupName = snapshotObj.getReplicationGroupInstance(); CIMObjectPath groupSynchronized = _cimPath.getGroupSynchronizedPath(storage, consistencyGroupName, snapshotGroupName); if (_helper.checkExists(storage, groupSynchronized, false, false) != null) { CIMObjectPath cimJob = null; if (storage.checkIfVmax3()) { if (snapshotObj.getSettingsInstance() == null) { throw DeviceControllerException.exceptions.snapSettingsInstanceNull(snapshotObj.getSnapsetLabel(), snapshotObj .getId().toString()); } // there could only be one restored snapshot per device at a time // terminate any pre-existing one in favor of the new one terminateAnyRestoreSessions(storage, snapshotObj, snapshot, taskCompleter); CIMObjectPath settingsPath = _cimPath.getGroupSynchronizedSettingsPath(storage, consistencyGroupName, snapshotObj.getSettingsInstance()); cimJob = _helper .callModifySettingsDefineState(storage, _helper.getRestoreFromSettingsStateInputArguments(settingsPath, false)); } else { CIMArgument[] restoreCGSnapInput = _helper.getRestoreFromReplicaInputArguments(groupSynchronized); cimJob = _helper.callModifyReplica(storage, restoreCGSnapInput); } ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockRestoreSnapshotJob(cimJob, storage.getId(), taskCompleter))); } else { ServiceError error = DeviceControllerErrors.smis.unableToFindSynchPath(consistencyGroupName); taskCompleter.error(_dbClient, error); } } catch (Exception e) { String message = String.format("Generic exception when trying to restoring snapshots from consistency group on array %s", storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("restoreGroupSnapshots", e.getMessage()); taskCompleter.error(_dbClient, error); } } private void rollbackCreateSnapshot(final StorageSystem storage, final CIMObjectPath targetGroupPath, final List<String> targetDeviceIds, final TaskCompleter taskCompleter) 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); } // Remove target devices if (targetDeviceIds != null && !targetDeviceIds.isEmpty()) { deleteTargetDevices(storage, targetDeviceIds.toArray(new String[targetDeviceIds.size()]), taskCompleter); } } 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); } } /** * * 1) Create Storage Setting: * -- Invoke CreateSetting() method on StorageCapabilities to create a default Setting. * -- Needs to be against the pool that the VDEVs will be created against * -- Then modify this setting via ModifyInstance() to set StorageExtentInitialUsage to "Delta Replica Target" * -- "Delta Replica Target" = 12 * -- For client.modifyInstance(CIMInstance, String[] propertyList) * * 2) Use this setting as a Goal on CreateOrModifyStoragePool(). This should create you VDEVs. * -- This will be an asynchronous operation!!! * -- Which pool to use? * -- We could have volumes (based on placement) on different pools * -- Select one of the volume pools? * -- Do we need to do placement for this? * -- Do we select some default pool? * * * @param storage - 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 thinlyProvisioned * @param count - Number of VDEVs to create * @param capacity - Size of the VDEVs to create * @param taskCompleter - Completer object used for task status update @return * @throws DeviceControllerException */ private List<String> kickOffTargetDevicesCreation(StorageSystem storage, CIMObjectPath volumeGroupPath, String sourceGroupName, String label, Boolean createInactive, boolean thinlyProvisioned, int count, URI storagePoolUri, long capacity, TaskCompleter taskCompleter) throws Exception { if (storage.checkIfVmax3()) { StoragePool storagePool = _dbClient.queryObject(StoragePool.class, storagePoolUri); CIMObjectPath poolPath = _helper.getPoolPath(storage, storagePool); return createTargetDevices(storage, poolPath, volumeGroupPath, null, sourceGroupName, null, createInactive, count, capacity, taskCompleter); } else if (thinlyProvisioned) { return ReplicationUtils.createTargetDevices(storage, sourceGroupName, label, createInactive, count, storagePoolUri, capacity, true, null, taskCompleter, _dbClient, _helper, _cimPath); } else { CIMObjectPath poolPath = findSnapStoragePoolOrThrow(storage); CIMInstance storageSetting = createStorageSetting(storage, poolPath); if (storageSetting == null) { final String errMsg = String.format( "Unable to find StoragePoolSetting for SnapStoragePool %s when creating target devices on array %s", poolPath.toString(), storage.getSerialNumber()); _log.error(errMsg); throw DeviceControllerExceptions.smis.unableToFindStoragePoolSetting(); } return createTargetDevices(storage, poolPath, null, storageSetting, sourceGroupName, label, createInactive, count, capacity, taskCompleter); } } /** * Method will search through the snap pools for the specified array * and return the CIMObjectPath for it. This will be the pool where * the target devices will be created for snapshot operation. * * @param storage - StorageSystem representing the array * @return CIMObjectPath - CIM Reference to SnapStoragePool to be used for * the target devices. * @throws WBEMException */ private CIMObjectPath findSnapStoragePoolOrNull(StorageSystem storage) throws WBEMException { CIMObjectPath snapPoolPath = null; CloseableIterator<CIMObjectPath> snapPools = null; try { CIMObjectPath systemPath = _cimPath.getStorageSystem(storage); snapPools = _helper.getAssociatorNames(storage, systemPath, null, storage.checkIfVmax3() ? StoragePool.PoolClassNames.Symm_SRPStoragePool.name() : SYMM_SNAP_STORAGE_POOL, null, null); // TODO: Get the first one for now, but could there be more than one? If so, what to do? if (snapPools.hasNext()) { snapPoolPath = snapPools.next(); _log.info(String.format("Found Symm_SnapStoragePool to use -> %s", snapPoolPath.toString())); } } finally { if (snapPools != null) { snapPools.close(); } } return snapPoolPath; } private CIMObjectPath findSnapStoragePoolOrThrow(StorageSystem storage) throws SmisException, WBEMException { CIMObjectPath objectPath = findSnapStoragePoolOrNull(storage); if (objectPath == null) { String msg = String.format("Failed to find an instance of Symm_SnapStoragePool for system %s", storage.getSerialNumber()); throw DeviceControllerExceptions.smis.noStoragePoolInstances(msg, null); } return objectPath; } /** * In order to create VDEV targets for snapshots, we need to have a StorageSetting * with a parameter set to enable it. This method will create an instance of this. * We will try to maintain a single StorageSetting for this purpose, per pool. To * that end, we'll first check for the existence of the setting (based on a * special name). * * @param storage - StorageSystem where the pool exists * @param poolPath - CIMObject representing the pool to allocate targets from * @return CIMInstance - null => error. Otherwise, will be the StorageSetting * instance for creating VDEVs against the pool. * @throws DeviceControllerException */ private CIMInstance createStorageSetting(StorageSystem storage, CIMObjectPath poolPath) throws Exception { CloseableIterator<CIMObjectPath> capabilities = null; CloseableIterator<CIMObjectPath> poolSettings = null; CIMInstance instance = null; try { // From the storage pool, get its capability (assuming there is only // one per pool). Get associated storage settings from the capability, // loop through each and find one that has the special name. If it // doesn't exist, create it and modify the StorageExtentInitialUsage // and ElementName. capabilities = _helper.getAssociatorNames(storage, poolPath, null, SYMM_STORAGE_POOL_CAPABILITIES, null, null); if (capabilities != null && capabilities.hasNext()) { CIMObjectPath poolCapabilities = capabilities.next(); poolSettings = _helper.getAssociatorNames(storage, poolCapabilities, null, SYMM_STORAGE_POOL_SETTING, null, null); CIMInstance foundVdevSettingForThisPool = null; while (poolSettings != null && poolSettings.hasNext()) { CIMInstance it = _helper.getInstance(storage, poolSettings.next(), false, false, PL_STORAGE_EXTENT_INITIAL_USAGE); int storageExtentInitialUsage = Integer.valueOf(CIMPropertyFactory .getPropertyValue(it, CP_STORAGE_EXTENT_INITIAL_USAGE)); if (storageExtentInitialUsage == DELTA_REPLICA_TARGET_VALUE) { // We found a setting that has the "Delta Replica Target" value // for the StorageExtentInitialUsage attribute foundVdevSettingForThisPool = it; break; } } if (foundVdevSettingForThisPool != null) { instance = foundVdevSettingForThisPool; _log.info(String.format("Found existing StorageSetting for VDEV %s", instance.toString())); } else { // It wasn't found ==> create it // TODO: How do we prevent concurrent operations from creating duplicates? _log.info("Could not find existing StorageSetting for VDEV, going to create it and modify it..."); CIMArgument[] inArgs = _helper.getCreateDefaultStoragePoolSettingsArguments(); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, poolCapabilities, CREATE_SETTING, inArgs, outArgs); CIMObjectPath newSetting = _cimPath.getCimObjectPathFromOutputArgs(outArgs, NEW_SETTING); instance = _cimPath.getStoragePoolVdevSettings(newSetting); _helper.modifyInstance(storage, instance, PL_STORAGE_EXTENT_INITIAL_USAGE); // Get the unique ID for display CIMInstance newSettingInstance = _helper.getInstance(storage, newSetting, false, false, PL_ONLY_EMC_UNIQUE_ID); String emcUniqueId = CIMPropertyFactory.getPropertyValue(newSettingInstance, CP_EMC_UNIQUE_ID); _log.info(String.format("Created StorageSetting for VDEV %s (EMCUniqueID = %s)", instance.toString(), emcUniqueId)); } } else { String message = String.format( "Could not find any %s instances for StoragePool %s. Will not be able to create a StorageSetting.", SYMM_STORAGE_POOL_CAPABILITIES, poolPath.toString()); _log.error(message); throw DeviceControllerExceptions.smis.noStoragePoolInstances(message, null); } } finally { if (capabilities != null) { capabilities.close(); } if (poolSettings != null) { poolSettings.close(); } } return instance; } /** * Method will invoke the SMI-S operation to create 'count' number of VDEVs of the * specified capacity. * * If any errors, taskCompleter will be updated. * * Note; This method will kick off of an asynchronous job. The SmisCreateVmaxCGTargetVolumesJob * encapsulates this. When the task completes successfully, it will continue the * work of completing the snapshot operation. * * @param storage - StorageSystem where VDEVs will be created * @param poolPath - CIMObject representing the pool to allocate targets from * @param storageSetting - Setting that allows for VDEV creation * @param label - Name to appl to each VDEV * @param createInactive - whether the snapshot needs to to be created with sync_active=true/false * @param count - Number of VDEVs * @param capacity - Size of each VDEV * @param taskCompleter - Completer object used for task status update * @throws DeviceControllerException * * @return - List of native Ids */ private List<String> createTargetDevices(StorageSystem storage, CIMObjectPath poolPath, CIMObjectPath volumeGroupPath, CIMInstance storageSetting, String sourceGroupName, String label, Boolean createInactive, int count, long capacity, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info(format("Creating target devices: Storage System: {0}, Consistency Group: {1}, Pool: {2}, Count: {3}", storage.getId(), sourceGroupName, poolPath, count)); try { CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storage); CIMArgument[] inArgs = null; if (storage.checkIfVmax3()) { inArgs = _helper.getCreateVolumesBasedOnVolumeGroupInputArguments(storage, poolPath, volumeGroupPath, label, count, capacity); } else { inArgs = _helper.getCreateVolumesBasedOnSettingInputArguments(storage, poolPath, storageSetting, label, count, capacity); } CIMArgument[] outArgs = new CIMArgument[5]; SmisCreateVmaxCGTargetVolumesJob job = new SmisCreateVmaxCGTargetVolumesJob(null, storage.getId(), sourceGroupName, label, createInactive, taskCompleter); _helper.invokeMethodSynchronously(storage, configSvcPath, _helper.createVolumesMethodName(storage), inArgs, outArgs, job); return job.getTargetDeviceIds(); } catch (Exception e) { final String errMsg = format("An error occurred when creating target devices on storage system {0}", storage.getId()); _log.error(errMsg, e); taskCompleter.error(_dbClient, SmisException.errors.methodFailed(_helper.createVolumesMethodName(storage), e.getMessage())); throw new SmisException(errMsg, e); } } /** * Method will invoke the SMI-S operation to delete the ReplicationGroup. * * @param system StorageSystem where the pool and volume exist * @param replicationGroupInstance InstanceID of the SMI-S ReplicationGroup * @throws Exception */ private void deleteTargetGroup(StorageSystem system, String replicationGroupInstance) throws Exception { /* * FIXME Inconsistencies with BlockSnapshot#getReplicationGroupInstance format * * CoprHD-created linked targets will have this field value in the format "<system-serial>+<instance>". * * Ingested linked targets will have this field value in the more simple format, "<instance>" */ if (system.getUsingSmis80()) { if (!replicationGroupInstance.contains("+")) { replicationGroupInstance = String.format("%s+%s", system.getSerialNumber(), replicationGroupInstance); } } CIMObjectPath groupPath = _cimPath.getReplicationGroupObjectPath(system, replicationGroupInstance); CIMArgument[] outArgs = new CIMArgument[5]; CIMArgument[] deleteGroupInArgs = _helper.getDeleteReplicationGroupInputArguments(system, groupPath, true); _helper.invokeMethod(system, _cimPath.getControllerReplicationSvcPath(system), DELETE_GROUP, deleteGroupInArgs, outArgs); } /** * 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 */ private void deleteTargetDevices(final StorageSystem storageSystem, final String[] deviceIds, final TaskCompleter taskCompleter) { _log.info(format("Removing target devices {0} from storage system {1}", Joiner.on("\t").join(deviceIds), storageSystem.getId())); try { if (storageSystem.checkIfVmax3()) { for (String deviceId : deviceIds) { try { _helper.removeVolumeFromParkingSLOStorageGroup(storageSystem, deviceId, false); } catch (Exception e) { _log.info("Failed to remove device {} from SLO SG. It may have already been removed", deviceId); } _log.info("Done invoking remove volume {} from parking SLO storage group", deviceId); } } final CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); final CIMObjectPath[] theElements = _cimPath.getVolumePaths(storageSystem, deviceIds); final CIMArgument[] inArgs = _helper.getReturnElementsToStoragePoolArguments(theElements, SmisConstants.CONTINUE_ON_NONEXISTENT_ELEMENT); final CIMArgument[] outArgs = new CIMArgument[5]; final SmisDeleteVmaxCGTargetVolumesJob job = new SmisDeleteVmaxCGTargetVolumesJob( null, storageSystem.getId(), deviceIds, taskCompleter); _helper.invokeMethodSynchronously(storageSystem, configSvcPath, RETURN_ELEMENTS_TO_STORAGE_POOL, inArgs, outArgs, job); } catch (Exception e) { _log.error( format("An error occurred when removing target devices {0} from storage system {1}", Joiner.on("\t").join(deviceIds), storageSystem.getId()), e); } } /** * 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 */ private void deleteTargetDeviceGroup(final StorageSystem storage, final CIMObjectPath targetGroupPath) { _log.info(format("Removing target device group {0} from storage system {1}", targetGroupPath, 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); } } /** * Routine will call SMI-S ModifyReplicaSynchronization to resume a synchronization instance. * The operation is a counter-intuitive; it will terminate a RESTORE session between * target and source. This was the suggestion based on input from SMI-S team. * See OPT 443785. * * @param storage [in] - StorageSystem object representing the array * @param from [in] - Should be the snapshot object * @param blockObject [in] - Should be the volume object * @param syncObject [in] - A CIMObjectPath representing the SMI-S synchronization object tying * from and blockObject together, along with other related consistency group members * @param taskCompleter [in] - TaskCompleter used for updating status of operation * @return true if the resume operation was successfully performed on the synchronization * @throws WBEMException */ private boolean resumeSnapshot(StorageSystem storage, BlockObject from, BlockObject blockObject, CIMObjectPath syncObject, TaskCompleter taskCompleter) throws WBEMException { boolean wasResumed = false; SmisBlockResumeSnapshotJob job = new SmisBlockResumeSnapshotJob(null, storage.getId(), new TaskCompleter() { @Override protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException { } }); CIMArgument[] result = new CIMArgument[5]; try { if (storage.checkIfVmax3()) { _helper.invokeMethodSynchronously(storage, _cimPath.getControllerReplicationSvcPath(storage), SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, _helper.getEMCResumeInputArguments(syncObject), result, job); } else if (storage.getUsingSmis80()) { /** * VMAX2 managed by 8.* SMIS * We need to pass Operation = 16 */ _helper.invokeMethodSynchronously(storage, _cimPath.getControllerReplicationSvcPath(storage), SmisConstants.MODIFY_REPLICA_SYNCHRONIZATION, _helper.getResumeSnapshotSynchronizationInputArguments(syncObject), result, job); } else { /** * VMAX2 managed by 4.6.2 SMI provider * We need to pass Operation = 14 */ _helper.invokeMethodSynchronously(storage, _cimPath.getControllerReplicationSvcPath(storage), SmisConstants.MODIFY_REPLICA_SYNCHRONIZATION, _helper.getResumeSynchronizationInputArguments(syncObject), result, job); } } catch (Exception e) { /* * May be ignored if message is about invalid device state, since when * dealing with multiple GroupSynchronized instances, we attempt to resume * all of them. */ _log.info("Encountered exception which may be ignored: {}", e.getMessage()); } if (job.isSuccess()) { _log.info("Synchronization was successfully resumed: {}", syncObject); wasResumed = true; } else { _log.info("Synchronization was not resumed and can be ignored: {}", syncObject); } return wasResumed; } /** * Helper to return a list of sync objects for a given volume that are in the 'Restored' state. * These objects represent the volume-to-snapshot relationship. * * NOTE: Multiple GroupSynchronized instances that are in the 'Mixed' state represent scenarios where * a source has multiple CG snapshots and only one may have been restored (the others could be "synchronized", * hence the mixed states). In this case, we simply treat Mixed state synchronizations as Restored * as we want to run ResyncReplica against at least one of the GroupSynchronized instances. * * @param storage [in] - StorageSystem object representing the array * @param blockObject [in] - Source block object * @return List of CIMObjectPaths representing Synchronization paths. * @throws Exception */ private Collection<CIMObjectPath> getRestoredOrMixedStateSyncObjects(StorageSystem storage, BlockObject blockObject) throws Exception { List<CIMObjectPath> syncObjects = null; if (blockObject.hasConsistencyGroup()) { syncObjects = getAllGroupSyncObjects(storage, blockObject); // TODO For any GroupSynchronized instances that have Mixed state, determine which ones // have underlying StorageSynchronized instances in state Restored and use only those. } else { syncObjects = getAllStorageSyncObjects(storage, blockObject); } return filter(syncObjects, hasRestoredOrMixedStatePredicate(storage)); } /** * Acquire StorageSynchronized instances that reference the given source block object. * * @param storage StorageSystem representing the array * @param blockObject BlockObject representing the source of replication. * @return A List of CIMObjectPath's for each StorageSynchronized instance. */ private List<CIMObjectPath> getAllStorageSyncObjects(StorageSystem storage, BlockObject blockObject) { List<CIMObjectPath> result = new ArrayList<>(); CloseableIterator<CIMObjectPath> it = null; try { it = _cimPath.getSyncObjects(storage, blockObject); while (it.hasNext()) { result.add(it.next()); } } finally { if (it != null) { it.close(); } } return result; } /** * Acquires ReplicationGroup's associated with the block object, then further acquires the GroupSynchonized * instances that reference them. * * @param storage StorageSystem that holds the GroupSynchronized instances. * @param blockObject BlockObject representing the source of replication. * @return A List of CIMObjectPath's for each GroupSynchronized instance. * @throws WBEMException */ private List<CIMObjectPath> getAllGroupSyncObjects(StorageSystem storage, BlockObject blockObject) throws WBEMException { List<CIMObjectPath> groupSyncPaths = new ArrayList<>(); CIMObjectPath blockObjectPath = _cimPath.getBlockObjectPath(storage, blockObject); CloseableIterator<CIMObjectPath> associatorNames = null; CloseableIterator<CIMObjectPath> groupSyncRefs = null; try { associatorNames = _helper.getAssociatorNames(storage, blockObjectPath, null, SE_REPLICATION_GROUP, null, null); while (associatorNames.hasNext()) { CIMObjectPath repGrpPath = associatorNames.next(); _log.info(String.format("Source %s has associated ReplicationGroup %s", blockObjectPath, repGrpPath)); groupSyncRefs = _helper.getReference(storage, repGrpPath, SE_GROUP_SYNCHRONIZED_RG_RG, null); while (groupSyncRefs.hasNext()) { CIMObjectPath grpSyncPath = groupSyncRefs.next(); _log.info(String.format("ReplicationGroup %s is referenced by %s", repGrpPath, grpSyncPath)); groupSyncPaths.add(grpSyncPath); } } } finally { if (associatorNames != null) { associatorNames.close(); } if (groupSyncRefs != null) { groupSyncRefs.close(); } } return groupSyncPaths; } private Predicate<CIMObjectPath> hasRestoredOrMixedStatePredicate(final StorageSystem storage) { return new Predicate<CIMObjectPath>() { @Override public boolean apply(CIMObjectPath path) { boolean result = false; try { CIMInstance instance = _helper.getInstance(storage, path, false, false, PS_COPY_STATE_AND_DESC); String copyState = instance.getPropertyValue(SmisConstants.CP_COPY_STATE).toString(); // GroupSynchronized does not have the EMCCopyStateDesc property Object descProperty = instance.getPropertyValue(SmisConstants.EMC_COPY_STATE_DESC); String copyStateDesc = (descProperty == null ? "null" : descProperty.toString()); _log.info(String.format("Sync %s has copyState %s (%s)", path.toString(), copyState, copyStateDesc)); result = copyState.equals(COPY_STATE_RESTORED_INT_VALUE) || copyState.equals(COPY_STATE_MIXED_INT_VALUE); } catch (Exception e) { String msg = String.format("Failed to acquire sync instance %s as part of Restore State check", path); _log.warn(msg, e); } return result; } }; } /** * Given a snapshot and a URI of its parent volume, look up any existing restore sessions and * terminate them. * * @param storage [in] - StorageSystem object representing the array * @param from [in] - Should be the snapshot object * @param volume [in] - Should be the volume URI * @param taskCompleter [in] - TaskCompleter used for updating status of operation * @throws Exception */ @Override public void terminateAnyRestoreSessions(StorageSystem storage, BlockObject from, URI volume, TaskCompleter taskCompleter) throws Exception { BlockObject blockObject = BlockObject.fetch(_dbClient, volume); Collection<CIMObjectPath> syncObjects = null; if (storage.checkIfVmax3()) { syncObjects = _helper.getSettingsDefineStatePaths(storage, blockObject, (BlockSnapshot) from); } else { syncObjects = getRestoredOrMixedStateSyncObjects(storage, blockObject); } for (CIMObjectPath syncObject : syncObjects) { resumeSnapshot(storage, from, blockObject, syncObject, taskCompleter); } } /** * Look up any snapshot objects associated with the give volume. For each, can to terminate * any existing restore sessions. * * @param storage [in] - StorageSystem object representing the array * @param volume [in] - Volume to use for lookup * @param taskCompleter [in] - TaskCompleter used for updating status of operation * @throws Exception */ private void terminateAnyRestoreSessionsForVolume(StorageSystem storage, BlockObject volume, TaskCompleter taskCompleter) throws Exception { if (storage.checkIfVmax3()) { terminateAnyRestoreSessions(storage, null, volume.getId(), taskCompleter); return; } NamedElementQueryResultList snapshots = new NamedElementQueryResultList(); _dbClient.queryByConstraint( ContainmentConstraint.Factory.getVolumeSnapshotConstraint(volume.getId()), snapshots); for (NamedElementQueryResultList.NamedElement ne : snapshots) { BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, ne.getId()); if (snapshot != null && !snapshot.getInactive()) { CIMObjectPath syncObjectPath = _cimPath.getSyncObject(storage, snapshot); if (_helper.checkExists(storage, syncObjectPath, false, false) != null) { terminateAnyRestoreSessions(storage, volume, snapshot.getId(), taskCompleter); } } } } /** * Implementation for a single volume snapshot resynchronization. * * @param storage [required] - StorageSystem object representing the array * @param volume [required] - Volume URI for the volume to be restored * @param snapshot [required] - BlockSnapshot URI representing the previously created * snap for the volume * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void resyncSingleVolumeSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { if (storage.checkIfVmax3()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } callEMCRefreshIfRequired(_dbClient, _helper, storage, Arrays.asList(snapshot)); BlockSnapshot from = _dbClient.queryObject(BlockSnapshot.class, snapshot); CIMObjectPath syncObjectPath = _cimPath.getSyncObject(storage, from); CIMObjectPath cimJob = _helper.callModifyReplica(storage, _helper.getResyncSnapshotWithWaitInputArguments(syncObjectPath)); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockResyncSnapshotJob(cimJob, storage.getId(), taskCompleter))); } catch (WBEMException e) { String message = String.format("Error encountered when trying to resync snapshot %s on array %s", snapshot.toString(), storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, error); } catch (Exception e) { String message = String.format("Generic exception when trying to resync snapshot %s on array %s", snapshot.toString(), storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("resyncSingleVolumeSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } @Override public void resyncGroupSnapshots(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { callEMCRefreshIfRequired(_dbClient, _helper, storage, Arrays.asList(snapshot)); if (storage.checkIfVmax3()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); // Check if the consistency group exists String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(snapshotObj, _dbClient); storage = findProviderFactory.withGroup(storage, consistencyGroupName).find(); if (storage == null) { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } String snapshotGroupName = snapshotObj.getReplicationGroupInstance(); CIMObjectPath groupSynchronized = _cimPath.getGroupSynchronizedPath(storage, consistencyGroupName, snapshotGroupName); if (_helper.checkExists(storage, groupSynchronized, false, false) != null) { CIMObjectPath cimJob = null; CIMArgument[] restoreCGSnapInput = _helper.getResyncSnapshotWithWaitInputArguments(groupSynchronized); cimJob = _helper.callModifyReplica(storage, restoreCGSnapInput); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockResyncSnapshotJob(cimJob, storage.getId(), taskCompleter))); } else { ServiceError error = DeviceControllerErrors.smis.unableToFindSynchPath(consistencyGroupName); taskCompleter.error(_dbClient, error); } } catch (Exception e) { String message = String.format("Generic exception when trying to resynchronizing consistency group snapshots on array %s", storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("resyncGroupSnapshots", e.getMessage()); taskCompleter.error(_dbClient, error); } } @Override public void establishVolumeSnapshotGroupRelation(StorageSystem storage, URI sourceVolume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("establishVolumeSnapshotGroupRelation operation START"); try { /** * get groupPath for source volume * get groupPath for snapshot * get snapshots belonging to the same Replication Group * get Element synchronizations between volumes and snapshots * call CreateGroupReplicaFromElementSynchronizations */ BlockSnapshot snapshotObj = _dbClient.queryObject(BlockSnapshot.class, snapshot); Volume volumeObj = _dbClient.queryObject(Volume.class, sourceVolume); CIMObjectPath srcRepSvcPath = _cimPath.getControllerReplicationSvcPath(storage); String volumeGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(volumeObj, _dbClient); CIMObjectPath volumeGroupPath = _cimPath.getReplicationGroupPath(storage, volumeGroupName); CIMObjectPath snapshotGroupPath = _cimPath.getReplicationGroupPath(storage, snapshotObj.getReplicationGroupInstance()); // Check if snapshot is referenced by a BlockSnapshotSession // if so, we must pass in the RelationshipName with the value of the session name. // NB. a SourceGroup aspect must exist List<BlockSnapshotSession> snapshotSessions = queryActiveResourcesByConstraint(_dbClient, BlockSnapshotSession.class, getLinkedTargetSnapshotSessionConstraint(snapshot)); String relationshipName = null; if (!snapshotSessions.isEmpty()) { relationshipName = snapshotSessions.get(0).getSessionLabel(); _log.info("Found snapshot session relationship: {}", relationshipName); } CIMObjectPath groupSynchronizedPath = _cimPath.getGroupSynchronized(volumeGroupPath, snapshotGroupPath); CIMInstance syncInstance = _helper.checkExists(storage, groupSynchronizedPath, false, false); if (syncInstance == null) { // get all snapshots belonging to a Replication Group. There may be multiple snapshots available for a Volume List<BlockSnapshot> snapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(snapshotObj, _dbClient); List<CIMObjectPath> elementSynchronizations = new ArrayList<CIMObjectPath>(); for (BlockSnapshot snapshotObject : snapshots) { Volume volume = _dbClient.queryObject(Volume.class, snapshotObject.getParent()); elementSynchronizations.add(_cimPath.getStorageSynchronized(storage, volume, storage, snapshotObject)); } _log.info("Creating Group synchronization between volume group and snapshot group"); CIMArgument[] inArgs = _helper.getCreateGroupReplicaFromElementSynchronizationsForSRDFInputArguments(volumeGroupPath, snapshotGroupPath, elementSynchronizations, relationshipName); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, srcRepSvcPath, SmisConstants.CREATE_GROUP_REPLICA_FROM_ELEMENT_SYNCHRONIZATIONS, inArgs, outArgs); // No Job returned } else { _log.info("Link already established.."); } taskCompleter.ready(_dbClient); } catch (Exception e) { String msg = String.format( "Failed to establish group relation between volume group and snapshot group. Volume: %s, Snapshot: %s", sourceVolume, snapshot); _log.error(msg, e); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, serviceError); } } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void createSnapshotSession(StorageSystem system, URI snapSessionURI, TaskCompleter completer) throws DeviceControllerException { if (system.checkIfVmax3()) { // Only supported for VMAX3 storage systems. try { _log.info("Create snapshot session operation START"); BlockSnapshotSession snapSession = _dbClient.queryObject(BlockSnapshotSession.class, snapSessionURI); URI sourceObjURI = snapSession.getParent().getURI(); BlockObject sourceObj = BlockObject.fetch(_dbClient, sourceObjURI); // Need to terminate an restore sessions, so that we can // restore from the same snapshot multiple times terminateAnyRestoreSessionsForVolume(system, sourceObj, completer); URI tenantURI = null; if (URIUtil.isType(sourceObjURI, Volume.class)) { tenantURI = ((Volume) sourceObj).getTenant().getURI(); } else { Volume sourceObjParent = _dbClient.queryObject(Volume.class, ((BlockSnapshot) sourceObj).getParent().getURI()); tenantURI = sourceObjParent.getTenant().getURI(); } TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, tenantURI); String tenantName = tenant.getLabel(); String snapSessionLabelToUse = _nameGenerator.generate(tenantName, snapSession.getSessionLabel(), snapSessionURI.toString(), '-', SmisConstants.MAX_SMI80_SNAPSHOT_NAME_LENGTH); CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); CIMObjectPath sourceObjPath = _cimPath.getBlockObjectPath(system, sourceObj); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getCreateSynchronizationAspectInput(sourceObjPath, false, snapSessionLabelToUse, new Integer( SmisConstants.MODE_SYNCHRONOUS)); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.CREATE_SYNCHRONIZATION_ASPECT, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockSnapshotSessionCreateJob(jobPath, system.getId(), completer))); } catch (Exception e) { _log.error("Exception creating snapshot session ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } else { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } /** * {@inheritDoc} */ @Override public void createGroupSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer) throws DeviceControllerException { if (system.checkIfVmax3()) { _log.info("Create snapshot session group operation START"); BlockSnapshotSession snapSession = _dbClient.queryObject(BlockSnapshotSession.class, snapSessionURI); BlockConsistencyGroup consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, snapSession.getConsistencyGroup()); TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, consistencyGroup.getTenant().getURI()); String tenantName = tenant.getLabel(); final String label = _nameGenerator.generate(tenantName, snapSession.getSessionLabel(), snapSessionURI.toString(), '-', SmisConstants.MAX_SMI80_SNAPSHOT_NAME_LENGTH); CIMObjectPath groupPath = _cimPath.getReplicationGroupPath(system, groupName); try { CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); CIMArgument[] outArgs = new CIMArgument[5]; CIMArgument[] inArgs = _helper.getCreateSynchronizationAspectForGroupInput(groupPath, false, label, new Integer(SmisConstants.MODE_SYNCHRONOUS)); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.CREATE_SYNCHRONIZATION_ASPECT, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob( new SmisBlockSnapshotSessionCGCreateJob(jobPath, system.getId(), completer))); } catch (Exception e) { _log.error("Exception creating group snapshot session ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } _log.info("Create snapshot session group operation FINISH"); } else { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void linkSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI, String copyMode, Boolean targetExists, TaskCompleter completer) throws DeviceControllerException { if (system.checkIfVmax3()) { // Only supported for VMAX3 storage systems. try { _log.info("Link new target {} to snapshot session {} START", snapshotURI, snapSessionURI); CIMObjectPath sourcePath = null; CIMObjectPath targetPath = null; BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI); URI sourceObjURI = snapshot.getParent().getURI(); BlockObject sourceObj = BlockObject.fetch(_dbClient, sourceObjURI); if (!targetExists) { // If we are linking a new target to the session, the block snapshot // parent must always be a volume because we don't support snapshots // of snapshot. if (URIUtil.isType(sourceObjURI, Volume.class)) { // Provision the new target volume. Volume sourceVolume = (Volume) sourceObj; CIMObjectPath volumeGroupPath = _helper.getVolumeGroupPath(system, system, sourceVolume, null); // COP-17240: For VMAX3, we will derive the target volumes from the source volumes SRP Pool CIMObjectPath poolPath = _helper.getVolumeStoragePoolPath(system, sourceVolume); TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, sourceVolume.getTenant().getURI()); String tenantName = tenant.getLabel(); String label = _nameGenerator.generate(tenantName, snapshot.getLabel(), snapshotURI.toString(), '-', SmisConstants.MAX_SMI80_SNAPSHOT_NAME_LENGTH); List<String> targetDeviceIds = createTargetDevices(system, poolPath, volumeGroupPath, null, "SingleSnapshot", label, Boolean.FALSE, 1, sourceVolume.getCapacity(), completer); if (targetDeviceIds.isEmpty()) { throw DeviceControllerException.exceptions.createTargetForSnapshotSessionFailed(snapSessionURI.toString()); } sourcePath = _cimPath.getVolumePath(system, sourceVolume.getNativeId()); targetPath = _cimPath.getVolumePath(system, targetDeviceIds.get(0)); // Set the native id into the snapshot. This will allow a rollback // to delete the target if we subsequently fail to link the target // to the array snapshot. String targetDeviceId = targetDeviceIds.get(0); snapshot.setNativeId(targetDeviceId); _dbClient.updateObject(snapshot); } else { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } else { // When the passed flag indicates the target exists and just needs to be // linked, this is the special case where we link a source volume to a snapshot // session of a linked target volume for the purpose of restoring the source // volume from the linked target volume for VMAX3. In this case, the source // of the passed snapshot is itself a BlockSnapshot. sourcePath = _cimPath.getBlockObjectPath(system, sourceObj); targetPath = _cimPath.getBlockObjectPath(system, snapshot); } // Now link the target to the array snapshot represented by the session. CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); BlockSnapshotSession snapSession = _dbClient.queryObject(BlockSnapshotSession.class, snapSessionURI); String syncAspectPath = snapSession.getSessionInstance(); CIMObjectPath settingsStatePath = _cimPath.getSyncSettingsPath(system, sourcePath, syncAspectPath); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getModifySettingsDefinedStateForLinkTargets(system, settingsStatePath, targetPath, copyMode); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockSnapshotSessionLinkTargetJob(jobPath, system.getId(), snapshotURI, copyMode, completer))); } catch (Exception e) { _log.error("Exception creating and linking snapshot session target", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } else { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } @Override public void linkSnapshotSessionTargetGroup(StorageSystem system, URI snapshotSessionURI, List<URI> snapSessionSnapshotURIs, String copyMode, Boolean targetsExist, TaskCompleter completer) throws DeviceControllerException { _log.info("Link new target group to snapshot session group START"); CIMObjectPath targetGroupPath = null; List<String> targetDeviceIds = new ArrayList<>(); // Gather all snapshots to be created List<URI> snapshotUris = snapSessionSnapshotURIs; List<BlockSnapshot> snapshots = newArrayList(_dbClient.queryIterativeObjects(BlockSnapshot.class, snapshotUris)); final Map<URI, BlockSnapshot> uriToSnapshot = new HashMap<>(); BlockSnapshot sampleSnapshot = snapshots.get(0); BlockObject sampleParent = BlockObject.fetch(_dbClient, sampleSnapshot.getParent().getURI()); try { String sourceGroupName; String targetGroupName; if (!targetsExist) { // This is the normal scenario for linking group targets to a group snapshot session. sourceGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sampleParent, _dbClient); // Group snapshots parent volumes by their pool and size Map<String, List<Volume>> volumesBySizeMap = new HashMap<>(); for (BlockSnapshot target : snapshots) { uriToSnapshot.put(target.getId(), target); Volume parent = _dbClient.queryObject(Volume.class, target.getParent().getURI()); String key = parent.getPool() + "-" + parent.getCapacity(); if (volumesBySizeMap.containsKey(key)) { volumesBySizeMap.get(key).add(parent); } else { volumesBySizeMap.put(key, newArrayList(parent)); } } // Create snapshot target volumes for (Entry<String, List<Volume>> entry : volumesBySizeMap.entrySet()) { final List<Volume> volumes = entry.getValue(); final Volume volume = volumes.get(0); final URI poolId = volume.getPool(); // get respective group path for volume storage pool CIMObjectPath volumeGroupPath = _helper.getVolumeGroupPath(system, system, volume, null); // Create target devices based on the array model final List<String> newDeviceIds = kickOffTargetDevicesCreation(system, volumeGroupPath, sourceGroupName, null, false, true, volumes.size(), poolId, volume.getCapacity(), completer); targetDeviceIds.addAll(newDeviceIds); } // Create target device group targetGroupPath = ReplicationUtils.createTargetDeviceGroup(system, sourceGroupName, targetDeviceIds, completer, _dbClient, _helper, _cimPath, SYNC_TYPE.SNAPSHOT); _log.info("Created target device group: {}", targetGroupPath); targetGroupName = (String) targetGroupPath.getKeyValue(CP_INSTANCE_ID); // Update the snapshots with the ReplicationGroup InstanceID for (BlockSnapshot snapshot : snapshots) { snapshot.setReplicationGroupInstance(targetGroupName); } _dbClient.updateObject(snapshots); } else { // If the targets exist, this is the restore linked target scenario where we create // a temporary group snapshot session on the linked target group and then linked the // source volume group to this temporary session. The source volumes and source group // already exist. First we setup of the snapshot map. for (BlockSnapshot target : snapshots) { uriToSnapshot.put(target.getId(), target); } // The parent in this case is a BlockSnapshot and the source group is the // replication group for the snapshot. We eliminate the system prefix and // serial number from the replication group, to get simply the group name // as in the case above. sourceGroupName = ((BlockSnapshot) sampleParent).getReplicationGroupInstance(); int groupNameStartIndex = sourceGroupName.indexOf("+") + 1; sourceGroupName = sourceGroupName.substring(groupNameStartIndex); // The target in this case is actually a source volume and the target group // is the source volume group, which we can get from the parent's replication group instance. // Note that we can use the sample parent because it references the same // repliaction group as the source volume. targetGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sampleParent, _dbClient); // Get the CIM object path for the target group. targetGroupPath = _cimPath.getReplicationGroupPath(system, targetGroupName); } // Now link the target group to the array snapshots represented by the session. CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); BlockSnapshotSession snapSession = _dbClient.queryObject(BlockSnapshotSession.class, snapshotSessionURI); String syncAspectPath = snapSession.getSessionInstance(); CIMObjectPath settingsStatePath = _cimPath.getGroupSynchronizedSettingsPath(system, sourceGroupName, syncAspectPath); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getModifySettingsDefinedStateForLinkTargetGroup(system, settingsStatePath, targetGroupPath, copyMode); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); SmisBlockSnapshotSessionLinkTargetGroupJob job = new SmisBlockSnapshotSessionLinkTargetGroupJob(jobPath, system.getId(), completer); job.setSourceGroupName(sourceGroupName); job.setTargetGroupName(targetGroupName); job.setSnapSessionInstance(snapSession.getSessionInstance()); Map<String, URI> srcNativeIdToSnapshot = Maps.uniqueIndex(snapshotUris, new Function<URI, String>() { @Override public String apply(URI input) { return uriToSnapshot.get(input).getSourceNativeId(); } }); job.setSrcNativeIdToSnapshotMap(srcNativeIdToSnapshot); ControllerServiceImpl.enqueueJob(new QueueJob(job)); _log.info("Link new target group to snapshot session group FINISH"); } catch (Exception e) { _log.error("Exception creating and linking snapshot session targets", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void relinkSnapshotSessionTarget(StorageSystem system, URI tgtSnapSessionURI, URI snapshotURI, TaskCompleter completer) throws DeviceControllerException { // Only supported for VMAX3 storage systems. if (!system.checkIfVmax3()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { _log.info("Re-link target {} to snapshot session {} START", snapshotURI, tgtSnapSessionURI); BlockSnapshotSession tgtSnapSession = _dbClient.queryObject(BlockSnapshotSession.class, tgtSnapSessionURI); BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI); URI sourceURI = tgtSnapSession.getParent().getURI(); BlockObject sourceObj = BlockObject.fetch(_dbClient, sourceURI); CIMObjectPath sourcePath = _cimPath.getVolumePath(system, sourceObj.getNativeId()); CIMObjectPath syncObjPath = getSyncObject(system, snapshot, sourceObj); boolean targetLinkedInCopyMode = isTargetOrGroupCopyMode(system, syncObjPath); CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); String syncAspectPath = tgtSnapSession.getSessionInstance(); CIMObjectPath settingsStatePath = _cimPath.getSyncSettingsPath(system, sourcePath, syncAspectPath); CIMObjectPath targetDevicePath = _cimPath.getBlockObjectPath(system, snapshot); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getModifySettingsDefinedStateForRelinkTargets(system, settingsStatePath, targetDevicePath, targetLinkedInCopyMode); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockSnapshotSessionRelinkTargetJob(jobPath, system.getId(), completer))); } catch (Exception e) { _log.error("Exception re-linking snapshot session", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void relinkSnapshotSessionTargetGroup(StorageSystem system, URI tgtSnapSessionURI, URI snapshotURI, TaskCompleter completer) throws DeviceControllerException { // Only supported for VMAX3 storage systems. if (!system.checkIfVmax3()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { _log.info("Re-link target {} to snapshot session {} START", snapshotURI, tgtSnapSessionURI); BlockSnapshotSession tgtSnapSession = _dbClient.queryObject(BlockSnapshotSession.class, tgtSnapSessionURI); String syncAspectPath = tgtSnapSession.getSessionInstance(); BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI); String groupName = ControllerUtils.extractGroupName(snapshot.getReplicationGroupInstance()); CIMObjectPath replicationGroupPath = _cimPath.getReplicationGroupPath(system, groupName); // get source group name from the session. String sourceGroupName = tgtSnapSession.getReplicationGroupInstance(); CIMObjectPath settingsStatePath = _cimPath.getGroupSynchronizedSettingsPath(system, sourceGroupName, syncAspectPath); // We need to know if the group was linked in copy mode or nocopy mode. CIMObjectPath groupSyncPath = _cimPath.getGroupSynchronizedPath(system, sourceGroupName, groupName); boolean targetGroupLinkedInCopyMode = isTargetOrGroupCopyMode(system, groupSyncPath); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getModifySettingsDefinedStateForRelinkTargetGroups(system, settingsStatePath, replicationGroupPath, targetGroupLinkedInCopyMode); CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockSnapshotSessionRelinkTargetJob(jobPath, system.getId(), completer))); } catch (Exception e) { _log.error("Exception re-linking snapshot session", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void unlinkSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI, Boolean deleteTarget, TaskCompleter completer) throws DeviceControllerException { // Only supported for VMAX3 storage systems. if (!system.checkIfVmax3()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { _log.info("Unlink target {} from snapshot session {} START", snapshotURI, snapSessionURI); BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, snapshotURI); String targetDeviceId = snapshot.getNativeId(); if (isNullOrEmpty(targetDeviceId)) { // The snapshot has no target device id. This means we must // have failed creating the target device for a link target // request and unlink target is being called in rollback. // Since the target was never created, we just return // success. _log.info("Snapshot target {} was never created.", snapshotURI); completer.ready(_dbClient); return; } // If the snapshot has a native id, then we at least // know the target device was created. Now we try and get // the sync object path representing the linked target so // that it can be detached. boolean syncObjectFound = false; List<BlockSnapshot> snapshots = null; BlockObject sourceObj = BlockObject.fetch(_dbClient, snapshot.getParent().getURI()); CIMObjectPath syncObjectPath = SmisConstants.NULL_CIM_OBJECT_PATH; if (snapshot.hasConsistencyGroup() && NullColumnValueGetter.isNotNullValue(snapshot.getReplicationGroupInstance())) { String replicationGroupName = snapshot.getReplicationGroupInstance(); String sourceReplicationGroupName = sourceObj.getReplicationGroupInstance(); List<CIMObjectPath> groupSyncs = getAllGroupSyncObjects(system, snapshot); if (groupSyncs != null && !groupSyncs.isEmpty()) { // Find the right one. We want the one where the replication groups for // the passed snapshot is the sync'd element. for (CIMObjectPath groupSynchronized : groupSyncs) { String syncElementPath = groupSynchronized.getKeyValue(SmisConstants.CP_SYNCED_ELEMENT).toString(); String systemElementPath = groupSynchronized.getKeyValue(SmisConstants.CP_SYSTEM_ELEMENT).toString(); if (syncElementPath.contains(replicationGroupName) && systemElementPath.contains(sourceReplicationGroupName)) { syncObjectPath = groupSynchronized; break; } } } snapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup( snapshot, _dbClient); } else { syncObjectPath = getSyncObject(system, snapshot, sourceObj); snapshots = Lists.newArrayList(snapshot); } if (!SmisConstants.NULL_CIM_OBJECT_PATH.equals(syncObjectPath)) { syncObjectFound = true; CIMArgument[] inArgs = _helper.getUnlinkBlockSnapshotSessionTargetInputArguments(syncObjectPath); CIMArgument[] outArgs = new CIMArgument[5]; CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); SmisBlockSnapshotSessionUnlinkTargetJob job = new SmisBlockSnapshotSessionUnlinkTargetJob(null, system.getId(), completer); _helper.invokeMethodSynchronously(system, replicationSvcPath, SmisConstants.MODIFY_REPLICA_SYNCHRONIZATION, inArgs, outArgs, job); // Succeeded in unlinking the target from the snapshot. for (BlockSnapshot snapshotToUpdate : snapshots) { snapshotToUpdate.setSettingsInstance(NullColumnValueGetter.getNullStr()); } _dbClient.updateObject(snapshots); } else { // For some reason we could not find the path for the // CIM_StorageSychronized instance for the linked target. // If the settingsInstance for the snapshot is not set, // this may mean we just failed a link target request // and unlink target is being called in rollback. In this // case we successfully created the target volume, but // failed to link the target to the snapshot, in which // case the settingsInstance would be null. Otherwise, // we could be retrying a failed unlink request. In this // case, we must have succeeded in unlinking the target // from the array snapshot, but failed attempting to // delete the target volume. If the unlink is successful, // the settingsInstance is reset to null. So, if the // settingsInstance is null, we move on without failing. // Otherwise, we should throw an exception. String settingsInstance = snapshot.getSettingsInstance(); if (NullColumnValueGetter.isNotNullValue(settingsInstance)) { throw DeviceControllerException.exceptions.couldNotFindSyncObjectToUnlinkTarget(targetDeviceId); } } if (deleteTarget) { _log.info("Delete target device {} :{}", targetDeviceId, snapshotURI); Collection<String> nativeIds = transform(snapshots, fctnBlockObjectToNativeID()); if (snapshot.hasConsistencyGroup() && NullColumnValueGetter.isNotNullValue(snapshot.getReplicationGroupInstance())) { try { checkReplicationGroupAccessibleOrFail(system, snapshot, _dbClient, _helper, _cimPath); deleteTargetGroup(system, snapshot.getReplicationGroupInstance()); } catch (DeviceControllerException | WBEMException e) { _log.info("Failed to delete the target group. It may have already been deleted."); } } // Ingested non-exported snapshot could be associated with SGs outside of ViPR, // remove snapshot from them before deleting it. for (BlockSnapshot snap : snapshots) { try { _helper.removeVolumeFromStorageGroupsIfVolumeIsNotInAnyMV(system, snap); } catch (Exception e) { _log.info("Failed to remove snap {} from storage groups. It may have already been removed.", snap.getNativeGuid()); } } callEMCRefresh(_helper, system, true); deleteTargetDevices(system, nativeIds.toArray(new String[] {}), completer); _log.info("Delete target device complete"); } else if (!syncObjectFound) { // Need to be sure the completer is called. completer.ready(_dbClient); } } catch (Exception e) { _log.error("Exception unlinking snapshot session target", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } /** * Determine the StorgeSynchronized path for the passed block snapshot where * the snapshot is the target device. * * @param system A reference to the system. * @param snapshot A reference to the snapshot. * @param sourceObj The snapshot source. * * @return The StorgeSynchronized path for the passed block snapshot. */ private CIMObjectPath getSyncObject(StorageSystem system, BlockSnapshot snapshot, BlockObject sourceObj) { CIMObjectPath returnPath = SmisConstants.NULL_CIM_OBJECT_PATH; CloseableIterator<CIMObjectPath> syncObjIter = null; try { syncObjIter = _cimPath.getSyncObjects(system, snapshot); while (syncObjIter.hasNext()) { CIMObjectPath syncObjPath = syncObjIter.next(); CIMObjectPath syncedElementPath = (CIMObjectPath) syncObjPath.getKey("SyncedElement").getValue(); String syncDeviceId = syncedElementPath.getKey("DeviceID").getValue().toString(); if (snapshot.getNativeId().equals(syncDeviceId)) { CIMObjectPath systemElementPath = (CIMObjectPath) syncObjPath.getKey("SystemElement").getValue(); String sysDeviceId = systemElementPath.getKey("DeviceID").getValue().toString(); if (sourceObj.getNativeId().equals(sysDeviceId)) { _log.info("Found synchronization {} for snapshot target {}", syncObjPath, snapshot.getNativeGuid()); returnPath = syncObjPath; break; } } } } finally { if (syncObjIter != null) { syncObjIter.close(); } } return returnPath; } /** * Determines if the target or target group represented by the passed synchronization object path * is linked to the snapshot session in "copy" mode. * * @param system A reference to the storage system. * @param syncObjPath The synchronization object path. * * @return true if the target is linked in "copy" mode, false otherwise. * * @throws Exception When an exception occurs getting the CIM_StorageSynchronized instance representing the linked target */ private boolean isTargetOrGroupCopyMode(StorageSystem system, CIMObjectPath syncObjPath) throws Exception { boolean isCopyMode = false; String[] props = new String[] { SmisConstants.CP_SYNC_TYPE }; CIMInstance syncObj = _helper.getInstance(system, syncObjPath, false, false, props); String syncType = syncObj.getProperty(SmisConstants.CP_SYNC_TYPE).getValue().toString(); if (String.valueOf(SYNC_TYPE.CLONE.getValue()).equals(syncType)) { isCopyMode = true; } return isCopyMode; } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void restoreSnapshotSession(StorageSystem system, URI snapSessionURI, TaskCompleter completer) throws DeviceControllerException { if (system.checkIfVmax3()) { // Only supported for VMAX3 storage systems. try { _log.info("Restore snapshot session {} START", snapSessionURI); BlockSnapshotSession snapSession = _dbClient.queryObject(BlockSnapshotSession.class, snapSessionURI); String syncAspectPath = snapSession.getSessionInstance(); CIMObjectPath settingsStatePath = null; BlockObject sourceObj = null; if (snapSession.hasConsistencyGroup() && NullColumnValueGetter.isNotNullValue(snapSession.getReplicationGroupInstance())) { _log.info("Restoring group snapshot session"); // We need a single source volume for the session. BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, snapSession.getConsistencyGroup()); List<Volume> nativeVolumes = BlockConsistencyGroupUtils.getActiveNativeVolumesInCG(cg, _dbClient); // get source group name from the session. String sourceGroupName = snapSession.getReplicationGroupInstance(); settingsStatePath = _cimPath.getGroupSynchronizedSettingsPath(system, sourceGroupName, syncAspectPath); for (Volume volume : nativeVolumes) { if (sourceGroupName.equals(volume.getReplicationGroupInstance())) { sourceObj = volume; break; // get source volume which matches session's RG name } } } else { _log.info("Restoring single volume snapshot session"); sourceObj = BlockObject.fetch(_dbClient, snapSession.getParent().getURI()); CIMObjectPath sourcePath = _cimPath.getVolumePath(system, sourceObj.getNativeId()); settingsStatePath = _cimPath.getSyncSettingsPath(system, sourcePath, syncAspectPath); } // Terminate restore sessions. terminateAnyRestoreSessions(system, null, sourceObj.getId(), completer); // Invoke SMI-S method to restore snapshot session. CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getRestoreFromSettingsStateInputArguments(settingsStatePath, true); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockSnapshotSessionRestoreJob(jobPath, system.getId(), completer))); } catch (Exception e) { _log.error("Exception restoring snapshot session", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } else { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override public void deleteSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer) throws DeviceControllerException { if (system.checkIfVmax3()) { // Only supported for VMAX3 storage systems. try { _log.info("Delete snapshot session {} START", snapSessionURI); BlockSnapshotSession snapSession = _dbClient.queryObject(BlockSnapshotSession.class, snapSessionURI); String syncAspectPath = snapSession.getSessionInstance(); if (NullColumnValueGetter.isNullValue(syncAspectPath)) { // If there is no session instance, it must have failed creation and // this is method is being called due to a rollback. _log.info("No session instance specified for snapshot session {}", snapSessionURI); completer.ready(_dbClient); } else { CIMObjectPath settingsStatePath = null; if (snapSession.hasConsistencyGroup() && NullColumnValueGetter.isNotNullValue(groupName)) { settingsStatePath = _cimPath.getGroupSynchronizedSettingsPath(system, groupName, syncAspectPath); } else { BlockObject sourceObj = BlockObject.fetch(_dbClient, snapSession.getParent().getURI()); CIMObjectPath sourcePath = _cimPath.getBlockObjectPath(system, sourceObj); settingsStatePath = _cimPath.getSyncSettingsPath(system, sourcePath, syncAspectPath); } CIMArgument[] inArgs = null; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getDeleteSettingsForSnapshotInputArguments(settingsStatePath, false); CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(system); _helper.invokeMethod(system, replicationSvcPath, SmisConstants.MODIFY_SETTINGS_DEFINE_STATE, inArgs, outArgs); CIMObjectPath jobPath = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockSnapshotSessionDeleteJob(jobPath, system.getId(), completer))); } } catch (Exception e) { _log.error("Exception deleting snapshot session", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); completer.error(_dbClient, error); } } else { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } }