/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource; import static com.emc.storageos.api.mapper.TaskMapper.toTask; import static com.emc.storageos.db.client.util.CommonTransformerFunctions.FCTN_MIRROR_TO_URI; import static com.emc.storageos.db.client.util.CommonTransformerFunctions.FCTN_STRING_TO_URI; import static com.emc.storageos.db.client.util.CommonTransformerFunctions.FCTN_VOLUME_URI_TO_STR; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.Collections2.transform; import static com.google.common.collect.Iterables.removeIf; import static java.lang.String.format; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.api.service.impl.placement.StorageScheduler; import com.emc.storageos.api.service.impl.placement.VolumeRecommendation; import com.emc.storageos.api.service.impl.placement.VpoolUse; import com.emc.storageos.api.service.impl.resource.fullcopy.BlockFullCopyManager; import com.emc.storageos.blockorchestrationcontroller.VolumeDescriptor; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockMirror; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.SynchronizationState; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.VolumeGroup; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.model.ResourceOperationTypeEnum; import com.emc.storageos.model.TaskList; import com.emc.storageos.model.TaskResourceRep; import com.emc.storageos.model.block.NativeContinuousCopyCreate; import com.emc.storageos.model.block.VirtualPoolChangeParam; import com.emc.storageos.model.block.VolumeCreate; import com.emc.storageos.model.block.VolumeDeleteTypeEnum; import com.emc.storageos.model.vpool.VirtualPoolChangeOperationEnum; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.svcs.errorhandling.resources.InternalException; import com.emc.storageos.util.ExportUtils; import com.emc.storageos.volumecontroller.BlockController; import com.emc.storageos.volumecontroller.ControllerException; import com.emc.storageos.volumecontroller.Recommendation; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; public class BlockMirrorServiceApiImpl extends AbstractBlockServiceApiImpl<StorageScheduler> { private static final Logger _log = LoggerFactory.getLogger(BlockMirrorServiceApiImpl.class); public BlockMirrorServiceApiImpl() { super(null); } private DefaultBlockServiceApiImpl _defaultBlockServiceApi; public void setDefaultBlockServiceApi(DefaultBlockServiceApiImpl defaultBlockServiceApi) { _defaultBlockServiceApi = defaultBlockServiceApi; } /** * {@inheritDoc} * * @throws ControllerException */ @Override public TaskList createVolumes(VolumeCreate param, Project project, VirtualArray neighborhood, VirtualPool cos, Map<VpoolUse, List<Recommendation>> volRecommendations, TaskList taskList, String task, VirtualPoolCapabilityValuesWrapper cosCapabilities) throws ControllerException { return _defaultBlockServiceApi.createVolumes(param, project, neighborhood, cos, volRecommendations, taskList, task, cosCapabilities); } /** * {@inheritDoc} * * @throws InternalException */ @Override public void deleteVolumes(final URI systemURI, final List<URI> volumeURIs, final String deletionType, final String task) throws InternalException { _log.info("Request to delete {} volume(s) with Mirror Protection", volumeURIs.size()); super.deleteVolumes(systemURI, volumeURIs, deletionType, task); } /** * {@inheritDoc} */ @Override public <T extends DataObject> String checkForDelete(T object, List<Class<? extends DataObject>> excludeTypes) { return null; } @Override public TaskList startNativeContinuousCopies(StorageSystem storageSystem, Volume sourceVolume, VirtualPool sourceVirtualPool, VirtualPoolCapabilityValuesWrapper capabilities, NativeContinuousCopyCreate param, String taskId) throws ControllerException { if (!((storageSystem.getUsingSmis80() && storageSystem.deviceIsType(Type.vmax)) || storageSystem.deviceIsType(Type.vnxblock))) { validateNotAConsistencyGroupVolume(sourceVolume, sourceVirtualPool); } TaskList taskList = new TaskList(); // Currently, this will create a single mirror and add it to the source volume // Two steps: first place the mirror and then prepare the mirror. List<Recommendation> volumeRecommendations = new ArrayList<Recommendation>(); // Prepare mirror. int volumeCounter = 1; int volumeCount = capabilities.getResourceCount(); String volumeLabel = param.getName(); List<Volume> preparedVolumes = new ArrayList<Volume>(); // If the requested volume is part of CG if (sourceVolume.isInCG()) { if (volumeCount > 1) { throw APIException.badRequests.invalidMirrorCountForVolumesInConsistencyGroup(); } URIQueryResultList cgVolumeList = new URIQueryResultList(); _dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumesByConsistencyGroup(sourceVolume.getConsistencyGroup()), cgVolumeList); // Process all CG volumes to create a corresponding Mirror // recommendation while (cgVolumeList.iterator().hasNext()) { Volume cgSourceVolume = _dbClient.queryObject(Volume.class, cgVolumeList.iterator().next()); _log.info("Processing volume {} in CG {}", cgSourceVolume.getId(), sourceVolume.getConsistencyGroup()); VirtualPool cgVolumeVPool = _dbClient.queryObject(VirtualPool.class, cgSourceVolume.getVirtualPool()); populateVolumeRecommendations(capabilities, cgVolumeVPool, cgSourceVolume, taskId, taskList, volumeCount, volumeCounter, volumeLabel, preparedVolumes, volumeRecommendations); } } else { // Source Volume without CG populateVolumeRecommendations(capabilities, sourceVirtualPool, sourceVolume, taskId, taskList, volumeCount, volumeCounter, volumeLabel, preparedVolumes, volumeRecommendations); } List<URI> mirrorList = new ArrayList<URI>(preparedVolumes.size()); for (Volume volume : preparedVolumes) { Operation op = _dbClient.createTaskOpStatus(BlockMirror.class, volume.getId(), taskId, ResourceOperationTypeEnum.ATTACH_BLOCK_MIRROR); volume.getOpStatus().put(taskId, op); TaskResourceRep volumeTask = toTask(volume, taskId, op); taskList.getTaskList().add(volumeTask); mirrorList.add(volume.getId()); } BlockController controller = getController(BlockController.class, storageSystem.getSystemType()); try { controller.attachNativeContinuousCopies(storageSystem.getId(), sourceVolume.getId(), mirrorList, taskId); } catch (ControllerException ce) { String errorMsg = format("Failed to start continuous copies on volume %s: %s", sourceVolume.getId(), ce.getMessage()); _log.error(errorMsg, ce); for (TaskResourceRep taskResourceRep : taskList.getTaskList()) { taskResourceRep.setState(Operation.Status.error.name()); taskResourceRep.setMessage(errorMsg); Operation statusUpdate = new Operation(Operation.Status.error.name(), errorMsg); _dbClient.updateTaskOpStatus(Volume.class, taskResourceRep.getResource().getId(), taskId, statusUpdate); } throw ce; } return taskList; } @Override public TaskList stopNativeContinuousCopies(StorageSystem storageSystem, Volume sourceVolume, List<URI> mirrors, String taskId) throws ControllerException { TaskList taskList = new TaskList(); List<URI> copiesToStop = null; List<BlockMirror> copies = null; Map<BlockMirror, Volume> groupMirrorSourceMap = null; boolean isCG = sourceVolume.isInCG(); if (isCG) { if (mirrors == null) { for (String uriStr : sourceVolume.getMirrors()) { BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, URI.create(uriStr)); if (!mirror.getInactive()) { groupMirrorSourceMap = getGroupMirrorSourceMap(mirror, sourceVolume); break; // only process one mirror group } } } else { groupMirrorSourceMap = getGroupMirrorSourceMap(mirrors.get(0), sourceVolume); } if (groupMirrorSourceMap == null || groupMirrorSourceMap.isEmpty()) { Operation op = new Operation(); op.ready(); op.setResourceType(ResourceOperationTypeEnum.DETACH_BLOCK_MIRROR); op.setMessage("No continuous copy can be detached"); _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, op); taskList.getTaskList().add(toTask(sourceVolume, taskId, op)); return taskList; } copiesToStop = new ArrayList<URI>(transform(groupMirrorSourceMap.keySet(), FCTN_MIRROR_TO_URI)); } else { List<BlockMirror> blockMirrors = null; if (mirrors != null) { blockMirrors = new ArrayList<BlockMirror>(); for (URI mirrorURI : mirrors) { BlockMirror blockMirror = _dbClient.queryObject(BlockMirror.class, mirrorURI); blockMirrors.add(blockMirror); } } copiesToStop = getCopiesToStop(blockMirrors, sourceVolume); // Ensure we don't attempt to stop any lingering inactive copies removeIf(copiesToStop, isMirrorInactivePredicate()); if (copiesToStop.size() == 0) { Operation op = new Operation(); op.ready(); op.setResourceType(ResourceOperationTypeEnum.DETACH_BLOCK_MIRROR); op.setMessage("No continuous copy can be detached"); _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, op); taskList.getTaskList().add(toTask(sourceVolume, taskId, op)); return taskList; } } copies = _dbClient.queryObject(BlockMirror.class, copiesToStop); // Stopped copies will be promoted to regular block volumes List<URI> promotees = preparePromotedVolumes(copies, taskList, taskId); if (!isCG) { String mirrorTargetCommaDelimList = Joiner.on(',').join(copiesToStop); Operation op = _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, ResourceOperationTypeEnum.DETACH_BLOCK_MIRROR, mirrorTargetCommaDelimList); taskList.getTaskList().add(toTask(sourceVolume, copies, taskId, op)); } else { populateTaskList(sourceVolume, groupMirrorSourceMap, taskList, taskId, ResourceOperationTypeEnum.DETACH_BLOCK_MIRROR); } BlockController controller = getController(BlockController.class, storageSystem.getSystemType()); try { controller.detachNativeContinuousCopies(storageSystem.getId(), copiesToStop, promotees, taskId); } catch (ControllerException ce) { String errorMsg = format("Failed to stop continuous copies for volume %s: %s", sourceVolume.getId(), ce.getMessage()); List<Volume> volumes = _dbClient.queryObject(Volume.class, promotees); for (Volume volume : volumes) { volume.setInactive(true); } _dbClient.persistObject(volumes); _log.error(errorMsg, ce); for (TaskResourceRep taskResourceRep : taskList.getTaskList()) { taskResourceRep.setState(Operation.Status.error.name()); taskResourceRep.setMessage(errorMsg); _dbClient.error(Volume.class, taskResourceRep.getResource().getId(), taskId, ce); } throw ce; } return taskList; } private List<URI> preparePromotedVolumes(List<BlockMirror> copiesToStop, TaskList taskList, String opId) { List<URI> promotedVolumes = new ArrayList<URI>(); for (BlockMirror copy : copiesToStop) { Volume v = new Volume(); v.setId(URIUtil.createId(Volume.class)); v.setLabel(copy.getLabel()); v.setProject(new NamedURI(copy.getProject().getURI(), copy.getProject().getName())); v.setTenant(new NamedURI(copy.getTenant().getURI(), copy.getTenant().getName())); _dbClient.createObject(v); Operation op = _dbClient.createTaskOpStatus(Volume.class, v.getId(), opId, ResourceOperationTypeEnum.PROMOTE_COPY_TO_VOLUME, copy.getId().toString()); taskList.getTaskList().add(toTask(v, Arrays.asList(copy), opId, op)); promotedVolumes.add(v.getId()); } return promotedVolumes; } /** * {@inheritDoc} * * @throws ControllerException */ @Override public TaskList pauseNativeContinuousCopies(StorageSystem storageSystem, Volume sourceVolume, List<BlockMirror> blockMirrors, Boolean sync, String taskId) throws ControllerException { TaskList taskList = new TaskList(); List<URI> mirrorUris = new ArrayList<>(); // Assume all continuous copies are to be paused List<BlockMirror> pausedMirrors = new ArrayList<>(); Map<BlockMirror, Volume> groupMirrorSourceMap = null; List<BlockMirror> mirrorsToProcess = null; boolean isCG = sourceVolume.isInCG(); if (isCG) { if (blockMirrors == null) { for (String uriStr : sourceVolume.getMirrors()) { BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, URI.create(uriStr)); if (mirrorIsPausable(mirror)) { groupMirrorSourceMap = getGroupMirrorSourceMap(mirror, sourceVolume); break; // only process one mirror group } } } else { groupMirrorSourceMap = getGroupMirrorSourceMap(blockMirrors.get(0), sourceVolume); } if (groupMirrorSourceMap == null || groupMirrorSourceMap.isEmpty()) { Operation op = new Operation(); op.ready(); op.setResourceType(ResourceOperationTypeEnum.FRACTURE_VOLUME_MIRROR); op.setMessage("No continuous copy can be paused"); _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, op); taskList.getTaskList().add(toTask(sourceVolume, taskId, op)); return taskList; } mirrorsToProcess = new ArrayList<BlockMirror>(groupMirrorSourceMap.keySet()); mirrorUris = new ArrayList<URI>(transform(mirrorsToProcess, FCTN_MIRROR_TO_URI)); } else { // Assume all continuous copies are to be paused mirrorsToProcess = blockMirrors; if (mirrorsToProcess == null) { mirrorsToProcess = new ArrayList<BlockMirror>(); for (String uriStr : sourceVolume.getMirrors()) { BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, URI.create(uriStr)); mirrorsToProcess.add(mirror); } } for (BlockMirror mirror : mirrorsToProcess) { if (mirrorIsResumable(mirror)) { // extract mirrors that are in "paused" state pausedMirrors.add(mirror); } else if (!mirrorIsPausable(mirror)) { // if there is a mirror is not in paused state, and not pausable, throw exception throw APIException.badRequests.cannotPauseContinuousCopyWithSyncState(mirror.getId(), mirror.getSyncState(), sourceVolume.getId()); } else if (mirrorIsResynchronizing(mirror)) { throw APIException.badRequests.cannotPauseContinuousCopyWhileResynchronizing(mirror.getId(), mirror.getSyncState(), sourceVolume.getId()); } else { // otherwise, place mirror a list... get ready to pause mirrorUris.add(mirror.getId()); } } } /* * if all mirrors are paused, then there is no task to do. * Return a successful task */ if (!pausedMirrors.isEmpty() && mirrorUris.isEmpty()) { // If the mirrors is already paused, there would be no need to queue another request to activate it again. Operation op = new Operation(); op.ready(); op.setResourceType(ResourceOperationTypeEnum.FRACTURE_VOLUME_MIRROR); op.setMessage("The continuous copies are already paused"); _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, op); taskList.getTaskList().add(toTask(sourceVolume, taskId, op)); } else { if (!isCG) { Collection<String> mirrorTargetIds = Collections2.transform(mirrorsToProcess, FCTN_VOLUME_URI_TO_STR); String mirrorTargetCommaDelimList = Joiner.on(',').join(mirrorTargetIds); Operation op = _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, ResourceOperationTypeEnum.FRACTURE_VOLUME_MIRROR, mirrorTargetCommaDelimList); taskList.getTaskList().add(toTask(sourceVolume, mirrorsToProcess, taskId, op)); } else { populateTaskList(sourceVolume, groupMirrorSourceMap, taskList, taskId, ResourceOperationTypeEnum.FRACTURE_VOLUME_MIRROR); } try { BlockController controller = getController(BlockController.class, storageSystem.getSystemType()); controller.pauseNativeContinuousCopies(storageSystem.getId(), mirrorUris, sync, taskId); } catch (ControllerException e) { String errorMsg = format("Failed to pause continuous copies for source volume %s", sourceVolume.getId()); _log.error(errorMsg, e); _dbClient.error(Volume.class, sourceVolume.getId(), taskId, e); } } return taskList; } /** * {@inheritDoc} */ @Override public TaskList resumeNativeContinuousCopies(StorageSystem storageSystem, Volume sourceVolume, List<BlockMirror> blockMirrors, String taskId) throws ControllerException { _log.info("START resume native continuous copies"); TaskList taskList = new TaskList(); List<URI> resumedMirrors = new ArrayList<URI>(); List<URI> mirrorURIs = new ArrayList<URI>(); Map<BlockMirror, Volume> groupMirrorSourceMap = null; List<BlockMirror> mirrorsToProcess = null; boolean isCG = sourceVolume.isInCG(); if (isCG) { if (blockMirrors == null) { for (String uriStr : sourceVolume.getMirrors()) { BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, URI.create(uriStr)); if (mirrorIsResumable(mirror)) { groupMirrorSourceMap = getGroupMirrorSourceMap(mirror, sourceVolume); break; // only process one mirror group } } } else { groupMirrorSourceMap = getGroupMirrorSourceMap(blockMirrors.get(0), sourceVolume); } if (groupMirrorSourceMap == null || groupMirrorSourceMap.isEmpty()) { Operation op = new Operation(); op.ready(); op.setResourceType(ResourceOperationTypeEnum.RESUME_VOLUME_MIRROR); op.setMessage("No continuous copy can be resumed"); _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, op); taskList.getTaskList().add(toTask(sourceVolume, taskId, op)); return taskList; } mirrorsToProcess = new ArrayList<BlockMirror>(groupMirrorSourceMap.keySet()); mirrorURIs = new ArrayList<URI>(transform(mirrorsToProcess, FCTN_MIRROR_TO_URI)); } else { // Assume all continuous copies are to be resumed mirrorsToProcess = blockMirrors; if (mirrorsToProcess == null) { mirrorsToProcess = new ArrayList<BlockMirror>(); for (String uriStr : sourceVolume.getMirrors()) { BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, URI.create(uriStr)); mirrorsToProcess.add(mirror); } } for (BlockMirror mirror : mirrorsToProcess) { if (mirrorIsPausable(mirror) || mirrorIsResynchronizing(mirror)) { // extract mirrors that are in resume state or resynchronizing resumedMirrors.add(mirror.getId()); } else if (!mirrorIsResumable(mirror)) { throw APIException.badRequests.cannotResumeContinuousCopyWithSyncState(mirror.getId(), mirror.getSyncState(), sourceVolume.getId()); } mirrorURIs.add(mirror.getId()); } } /* * if all mirrors are resumed/resynchronizing, then there is no task to do. * Return a successful task */ if (!resumedMirrors.isEmpty() && mirrorURIs.isEmpty()) { // If the mirrors is already resumed or resynchronizing, there would be no need to queue another request to // resume it again. Operation op = new Operation(); op.setResourceType(ResourceOperationTypeEnum.RESUME_VOLUME_MIRROR); op.setAssociatedResourcesField(Joiner.on(',').join(resumedMirrors)); op.ready("The continuous copies are already resumed or resynchronizing"); _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, op); taskList.getTaskList().add(toTask(sourceVolume, taskId, op)); } else { if (!isCG) { Collection<String> mirrorTargetIds = Collections2.transform(mirrorsToProcess, FCTN_VOLUME_URI_TO_STR); String mirrorTargetCommaDelimList = Joiner.on(',').join(mirrorTargetIds); Operation op = _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, ResourceOperationTypeEnum.RESUME_VOLUME_MIRROR, mirrorTargetCommaDelimList); taskList.getTaskList().add(toTask(sourceVolume, mirrorsToProcess, taskId, op)); } else { populateTaskList(sourceVolume, groupMirrorSourceMap, taskList, taskId, ResourceOperationTypeEnum.RESUME_VOLUME_MIRROR); } try { BlockController controller = getController(BlockController.class, storageSystem.getSystemType()); controller.resumeNativeContinuousCopies(storageSystem.getId(), mirrorURIs, taskId); } catch (ControllerException e) { String errorMsg = format("Failed to resume continuous copies for source volume %s", sourceVolume.getId()); _log.error(errorMsg, e); _dbClient.error(Volume.class, sourceVolume.getId(), taskId, e); } } return taskList; } /** * {@inheritDoc} */ @Override public TaskResourceRep establishVolumeAndNativeContinuousCopyGroupRelation( StorageSystem storageSystem, Volume sourceVolume, BlockMirror blockMirror, String taskId) throws ControllerException { _log.info("START establish Volume and Mirror group relation"); Operation op = _dbClient.createTaskOpStatus(Volume.class, sourceVolume .getId(), taskId, ResourceOperationTypeEnum.ESTABLISH_VOLUME_MIRROR, blockMirror .getId().toString()); try { BlockController controller = getController(BlockController.class, storageSystem.getSystemType()); controller.establishVolumeAndNativeContinuousCopyGroupRelation(storageSystem.getId(), sourceVolume.getId(), blockMirror.getId(), taskId); } catch (ControllerException e) { String errorMsg = format( "Failed to establish group relation between volume group and mirror group." + "Source volume: %s, Mirror: %s", sourceVolume.getId(), blockMirror.getId()); _log.error(errorMsg, e); _dbClient.error(Volume.class, sourceVolume.getId(), taskId, e); } return toTask(sourceVolume, Arrays.asList(blockMirror), taskId, op); } /** * {@inheritDoc} * * @throws ControllerException */ @SuppressWarnings("unchecked") @Override public TaskList deactivateMirror(StorageSystem storageSystem, URI mirrorURI, String taskId, String deleteType) throws ControllerException { _log.info("START: deactivate mirror"); TaskList taskList = new TaskList(); BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, mirrorURI); Volume sourceVolume = _dbClient.queryObject(Volume.class, mirror.getSource().getURI()); List<URI> mirrorURIs = new ArrayList<URI>(); boolean isCG = sourceVolume.isInCG(); List<URI> promotees = null; if (isCG) { // for group mirrors, deactivate task will detach and delete the mirror that user asked to deactivate, and // promote other mirrors // in the group Map<BlockMirror, Volume> groupMirrorSourceMap = getGroupMirrorSourceMap(mirror, sourceVolume); mirrorURIs = new ArrayList<URI>(transform(new ArrayList<BlockMirror>(groupMirrorSourceMap.keySet()), FCTN_MIRROR_TO_URI)); if (VolumeDeleteTypeEnum.VIPR_ONLY.name().equals(deleteType)) { // Create a task for each source/mirror pair. for (Entry<BlockMirror, Volume> entry : groupMirrorSourceMap.entrySet()) { Operation op = _dbClient.createTaskOpStatus(Volume.class, entry.getValue().getId(), taskId, ResourceOperationTypeEnum.DEACTIVATE_VOLUME_MIRROR, entry.getKey().getId().toString()); taskList.getTaskList().add(toTask(entry.getValue(), Arrays.asList(entry.getKey()), taskId, op)); } } else { // deactivate (detach and delete) mirrorURI Operation op = _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, ResourceOperationTypeEnum.DEACTIVATE_VOLUME_MIRROR, mirrorURI.toString()); taskList.getTaskList().add(toTask(sourceVolume, Arrays.asList(mirror), taskId, op)); // detach and promote other mirrors in the group groupMirrorSourceMap.remove(mirror); populateTaskList(sourceVolume, groupMirrorSourceMap, taskList, taskId, ResourceOperationTypeEnum.DETACH_BLOCK_MIRROR); // detached mirrors (except the one deleted), will be promoted to regular block volumes promotees = preparePromotedVolumes(new ArrayList<BlockMirror>(groupMirrorSourceMap.keySet()), taskList, taskId); } } else { // for single volume mirror, deactivate task will detach and delete the mirror mirrorURIs = Arrays.asList(mirror.getId()); Operation op = _dbClient.createTaskOpStatus(Volume.class, sourceVolume.getId(), taskId, ResourceOperationTypeEnum.DEACTIVATE_VOLUME_MIRROR, mirror.getId().toString()); taskList.getTaskList().add(toTask(sourceVolume, Arrays.asList(mirror), taskId, op)); } try { if (VolumeDeleteTypeEnum.VIPR_ONLY.name().equals(deleteType)) { _log.info("Perform ViPR-only delete for mirrors %s", mirrorURIs); // Perform any database cleanup that is required. cleanupForViPROnlyMirrorDelete(mirrorURIs); // Mark them inactive. _dbClient.markForDeletion(_dbClient.queryObject(BlockMirror.class, mirrorURIs)); // Update the task status for each snapshot to successfully completed. for (TaskResourceRep taskResourceRep : taskList.getTaskList()) { Volume taskVolume = _dbClient.queryObject(Volume.class, taskResourceRep.getResource().getId()); Operation op = taskVolume.getOpStatus().get(taskId); op.ready("Continuous copy succesfully deleted from ViPR"); taskVolume.getOpStatus().updateTaskStatus(taskId, op); _dbClient.updateObject(taskVolume); } } else { BlockController controller = getController(BlockController.class, storageSystem.getSystemType()); controller.deactivateMirror(storageSystem.getId(), mirrorURIs, promotees, isCG, taskId); } } catch (ControllerException e) { String errorMsg = format("Failed to deactivate continuous copy %s: %s", mirror.getId().toString(), e.getMessage()); _log.error(errorMsg); for (TaskResourceRep taskResourceRep : taskList.getTaskList()) { taskResourceRep.setState(Operation.Status.error.name()); taskResourceRep.setMessage(errorMsg); _dbClient.error(URIUtil.getModelClass(taskResourceRep.getResource().getId()), taskResourceRep.getResource().getId(), taskId, e); } // Mark the mirrors that would have been promoted inactive. if (promotees != null && !promotees.isEmpty()) { List<Volume> volumes = _dbClient.queryObject(Volume.class, promotees); for (Volume volume : volumes) { volume.setInactive(true); } _dbClient.updateObject(volumes); } } catch (Exception e) { String errorMsg = format("Failed to deactivate continuous copy %s: %s", mirror.getId().toString(), e.getMessage()); _log.error(errorMsg); ServiceCoded sc = APIException.internalServerErrors.genericApisvcError(errorMsg, e); for (TaskResourceRep taskResourceRep : taskList.getTaskList()) { taskResourceRep.setState(Operation.Status.error.name()); taskResourceRep.setMessage(sc.getMessage()); _dbClient.error(URIUtil.getModelClass(taskResourceRep.getResource().getId()), taskResourceRep.getResource().getId(), taskId, sc); } // Mark the mirrors that would have been promoted inactive. if (promotees != null && !promotees.isEmpty()) { List<Volume> volumes = _dbClient.queryObject(Volume.class, promotees); for (Volume volume : volumes) { volume.setInactive(true); } _dbClient.updateObject(volumes); } } return taskList; } /** * {@inheritDoc} */ @Override protected void cleanupForViPROnlyDelete(List<VolumeDescriptor> volumeDescriptors) { // Call super first. super.cleanupForViPROnlyDelete(volumeDescriptors); // Clean up the relationship between volumes that are full // copies and and their source volumes. BlockFullCopyManager.cleanUpFullCopyAssociations(volumeDescriptors, _dbClient); } /** * {@inheritDoc} */ @Override protected void cleanupForViPROnlyMirrorDelete(List<URI> mirrorURIs) { // Remove mirrors from ExportGroup(s) and ExportMask(s). for (URI mirrorURI : mirrorURIs) { ExportUtils.cleanBlockObjectFromExports(mirrorURI, true, _dbClient); } } @Override public TaskList changeVolumeVirtualPool(URI systemURI, Volume volume, VirtualPool virtualPool, VirtualPoolChangeParam cosChangeParam, String taskId) throws ControllerException { StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, systemURI); String systemType = storageSystem.getSystemType(); List<Volume> volumes = new ArrayList<Volume>(); volumes.add(volume); if (checkCommonVpoolUpdates(volumes, virtualPool, taskId)) { return null; } if (DiscoveredDataObject.Type.vnxblock.name().equals(systemType) || DiscoveredDataObject.Type.vmax.name().equals(systemType)) { URI original = volume.getVirtualPool(); // Update the volume with the new virtual pool volume.setVirtualPool(virtualPool.getId()); _dbClient.updateObject(volume); // Update the task String msg = format("VirtualPool changed from %s to %s for Volume %s", original, virtualPool.getId(), volume.getId()); Operation opStatus = new Operation(Operation.Status.ready.name(), msg); _dbClient.updateTaskOpStatus(Volume.class, volume.getId(), taskId, opStatus); } else { throw APIException.badRequests.unsupportedSystemType(systemType); } return null; } @Override public TaskList changeVolumeVirtualPool(List<Volume> volumes, VirtualPool vpool, VirtualPoolChangeParam vpoolChangeParam, String taskId) throws InternalException { TaskList taskList = createTasksForVolumes(vpool, volumes, taskId); // Check for common Vpool updates handled by generic code. It returns true if handled. if (checkCommonVpoolUpdates(volumes, vpool, taskId)) { return taskList; } for (Volume volume : volumes) { changeVolumeVirtualPool(volume.getStorageController(), volume, vpool, vpoolChangeParam, taskId); } return taskList; } private Predicate<URI> isMirrorInactivePredicate() { return new Predicate<URI>() { @Override public boolean apply(URI uri) { BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, uri); return mirror == null || mirror.getInactive(); } }; } private List<URI> getCopiesToStop(List<BlockMirror> blockMirrors, Volume sourceVolume) { List<URI> copiesToStop = new ArrayList<URI>(); if (blockMirrors == null || blockMirrors.isEmpty()) { copiesToStop.addAll(transform(sourceVolume.getMirrors(), FCTN_STRING_TO_URI)); } else { copiesToStop.addAll(transform(blockMirrors, FCTN_MIRROR_TO_URI)); } return copiesToStop; } private boolean mirrorIsPausable(BlockMirror mirror) { return mirror.getInactive() == false && !SynchronizationState.FRACTURED.toString().equals(mirror.getSyncState()); } private boolean mirrorIsResumable(BlockMirror mirror) { return !mirror.getInactive() && SynchronizationState.FRACTURED.toString().equals(mirror.getSyncState()); } private boolean mirrorIsResynchronizing(BlockMirror mirror) { return !mirror.getInactive() && SynchronizationState.RESYNCHRONIZING.toString().equals(mirror.getSyncState()); } /** * {@inheritDoc} */ @Override public List<VolumeDescriptor> getDescriptorsForVolumesToBeDeleted(URI systemURI, List<URI> volumeURIs, String deletionType) { List<VolumeDescriptor> volumeDescriptors = new ArrayList<VolumeDescriptor>(); for (URI volumeURI : volumeURIs) { VolumeDescriptor desc = new VolumeDescriptor( VolumeDescriptor.Type.BLOCK_DATA, systemURI, volumeURI, null, null); volumeDescriptors.add(desc); Volume volume = _dbClient.queryObject(Volume.class, volumeURI); addDescriptorsForMirrors(volumeDescriptors, volume); } return volumeDescriptors; } /** * {@inheritDoc} */ @Override protected List<VirtualPoolChangeOperationEnum> getVirtualPoolChangeAllowedOperations(Volume volume, VirtualPool volumeVirtualPool, VirtualPool newVirtualPool, StringBuffer notSuppReasonBuff) { return _defaultBlockServiceApi.getVirtualPoolChangeAllowedOperations(volume, volumeVirtualPool, newVirtualPool, notSuppReasonBuff); } /** * Populate the Recommendations for the given sourceVolume * * @param capabilities * @param sourceVolumeVPool * @param sourceVolume * @param taskId * @param taskList * @param volumeCount * @param volumeCounter * @param volumeLabel * @param preparedVolumes * @param volumeRecommendations */ private void populateVolumeRecommendations(VirtualPoolCapabilityValuesWrapper capabilities, VirtualPool sourceVolumeVPool, Volume sourceVolume, String taskId, TaskList taskList, Integer volumeCount, int volumeCounter, String volumeLabel, List<Volume> preparedVolumes, List<Recommendation> volumeRecommendations) { List<Recommendation> currentRecommendation = new ArrayList<Recommendation>(); VirtualPool mirrorVPool = sourceVolumeVPool; if (!isNullOrEmpty(sourceVolumeVPool.getMirrorVirtualPool())) { URI mirrorPoolUri = URI.create(sourceVolumeVPool.getMirrorVirtualPool()); if (!URIUtil.isNull(mirrorPoolUri)) { mirrorVPool = _dbClient.queryObject(VirtualPool.class, mirrorPoolUri); } } for (int i = 0; i < capabilities.getResourceCount(); i++) { VolumeRecommendation volumeRecommendation = new VolumeRecommendation( VolumeRecommendation.VolumeType.BLOCK_VOLUME, sourceVolume.getCapacity(), sourceVolumeVPool, sourceVolume.getVirtualArray()); volumeRecommendation.setId(sourceVolume.getId()); volumeRecommendation.addStoragePool(sourceVolume.getPool()); currentRecommendation.add(volumeRecommendation); } VirtualArray vArray = _dbClient.queryObject(VirtualArray.class, sourceVolume.getVirtualArray()); _scheduler.getRecommendationsForMirrors(vArray, mirrorVPool, capabilities, currentRecommendation); // only mirror will be prepared (the source already exist) _scheduler.prepareRecommendedVolumes(null, taskId, taskList, null, null, sourceVolumeVPool, volumeCount, currentRecommendation, null, volumeCounter, volumeLabel, preparedVolumes, capabilities, false); volumeRecommendations.addAll(currentRecommendation); } private Map<BlockMirror, Volume> getGroupMirrorSourceMap(URI mirrorUri, Volume sourceVolume) { return getGroupMirrorSourceMap(_dbClient.queryObject(BlockMirror.class, mirrorUri), sourceVolume); } private Map<BlockMirror, Volume> getGroupMirrorSourceMap(BlockMirror mirror, Volume sourceVolume) { Map<BlockMirror, Volume> mirrorSourceMap = new HashMap<BlockMirror, Volume>(); if (sourceVolume.isInCG()) { URIQueryResultList queryResults = new URIQueryResultList(); _dbClient.queryByConstraint(AlternateIdConstraint.Factory .getMirrorReplicationGroupInstanceConstraint(mirror .getReplicationGroupInstance()), queryResults); Iterator<URI> resultsIter = queryResults.iterator(); while (resultsIter.hasNext()) { URI uri = resultsIter.next(); if (uri.equals(mirror.getId())) { mirrorSourceMap.put(mirror, sourceVolume); } else { BlockMirror obj = _dbClient.queryObject(BlockMirror.class, uri); mirrorSourceMap.put(obj, _dbClient.queryObject(Volume.class, obj.getSource())); } } } return mirrorSourceMap; } /** * Populate the given TaskList with tasks. * * @param source * Source volume acted on from request * @param groupMirrorSourceMap * Map of mirrors to their source * @param taskList * TaskList * @param taskId * The task ID * @param operationType * The operation type */ private void populateTaskList(Volume source, Map<BlockMirror, Volume> groupMirrorSourceMap, TaskList taskList, String taskId, ResourceOperationTypeEnum operationType) { Set<URI> groupSet = new HashSet<URI>(); addTask(taskList, source, taskId, operationType); for (Entry<BlockMirror, Volume> entry : groupMirrorSourceMap.entrySet()) { BlockMirror mirror = entry.getKey(); Volume mirrorSource = entry.getValue(); if (source.isInCG() && null != taskList.getTaskList()) { groupSet.add(mirrorSource.getConsistencyGroup()); } } List<BlockConsistencyGroup> groups = _dbClient.queryObject(BlockConsistencyGroup.class, groupSet); for (BlockConsistencyGroup group : groups) { addTask(taskList, group, taskId, operationType); } } private void addTask(TaskList taskList, DataObject object, String taskId, ResourceOperationTypeEnum opType) { Operation op = _dbClient.createTaskOpStatus(object.getClass(), object.getId(), taskId, opType); taskList.addTask(toTask(object, taskId, op)); } @Override public List<VolumeDescriptor> createVolumesAndDescriptors(List<VolumeDescriptor> descriptors, String name, Long size, Project project, VirtualArray varray, VirtualPool vpool, List<Recommendation> recommendations, TaskList taskList, String task, VirtualPoolCapabilityValuesWrapper vpoolCapabilities) { // Not currently called from AbstractBlockServiceApiImpl.createVolumesAndDescriptors throw DeviceControllerException.exceptions.operationNotSupported(); } /* (non-Javadoc) * @see com.emc.storageos.api.service.impl.resource.BlockServiceApi#getReplicationGroupNames(com.emc.storageos.db.client.model.VolumeGroup) */ @Override public Collection<? extends String> getReplicationGroupNames(VolumeGroup group) { // mirror volumes won't be physically in applications so nothing to return here return new ArrayList<String>(); } }