Search in sources :

Example 6 with ExportMaskRemoveInitiatorCompleter

use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter in project coprhd-controller by CoprHD.

the class VPlexDeviceController method storageViewRemoveInitiators.

/**
 * Workflow step to remove an initiator from a single Storage View as given by the ExportMask URI.
 * Note there is a dependence on ExportMask name equaling the Storage View name.
 * Note that arguments must match storageViewRemoveInitiatorsMethod above (except stepId).
 *
 * @param vplexURI
 *            -- URI of Vplex Storage System.
 * @param exportGroupURI
 *            -- URI of Export Group.
 * @param exportMaskURI
 *            -- URI of one ExportMask. Call only processes indicaated mask.
 * @param initiatorURIs
 *            -- URIs of Initiators to be removed.
 * @param targetURIs
 *            -- optional targets to be removed from the Storage View.
 *            If non null, a list of URIs for VPlex front-end ports that will be removed from Storage View.
 * @param taskCompleter
 *            -- the task completer, used to find the rollback context,
 *               which will be non-null in the case of rollback
 * @param rollbackContextKey
 *            context key for rollback processing
 * @param stepId
 *            -- Workflow step id.
 * @throws WorkflowException
 */
public void storageViewRemoveInitiators(URI vplexURI, URI exportGroupURI, URI exportMaskURI, List<URI> initiatorURIs, List<URI> targetURIs, TaskCompleter taskCompleter, String rollbackContextKey, String stepId) throws WorkflowException {
    ExportMaskRemoveInitiatorCompleter completer = null;
    try {
        WorkflowStepCompleter.stepExecuting(stepId);
        List<URI> initiatorIdsToProcess = new ArrayList<>(initiatorURIs);
        completer = new ExportMaskRemoveInitiatorCompleter(exportGroupURI, exportMaskURI, initiatorURIs, stepId);
        StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
        ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
        VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
        String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
        Map<String, String> targetPortMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
        VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
        _log.info("Refreshing ExportMask {}", exportMask.getMaskName());
        VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortMap, _networkDeviceController);
        // get the context from the task completer, in case this is a rollback.
        if (taskCompleter != null && rollbackContextKey != null) {
            ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(rollbackContextKey);
            if (context != null) {
                // a non-null context means this step is running as part of a rollback.
                List<URI> addedInitiators = new ArrayList<>();
                if (context.getOperations() != null) {
                    _log.info("Handling removeInitiators as a result of rollback");
                    ListIterator<ExportOperationContextOperation> li = context.getOperations().listIterator(context.getOperations().size());
                    while (li.hasPrevious()) {
                        ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
                        if (operation != null && VplexExportOperationContext.OPERATION_ADD_INITIATORS_TO_STORAGE_VIEW.equals(operation.getOperation())) {
                            addedInitiators = (List<URI>) operation.getArgs().get(0);
                            _log.info("Removing initiators {} as part of rollback", Joiner.on(',').join(addedInitiators));
                        }
                    }
                }
                // Update the initiators in the task completer such that we update the export mask/group correctly
                for (URI initiator : initiatorIdsToProcess) {
                    if (addedInitiators == null || !addedInitiators.contains(initiator)) {
                        completer.removeInitiator(initiator);
                    }
                }
                if (addedInitiators == null || addedInitiators.isEmpty()) {
                    _log.info("There was no context found for add initiator. So there is nothing to rollback.");
                    completer.ready(_dbClient);
                    return;
                }
                // Change the list of initiators to process to the list
                // that successfully were added during addInitiators.
                initiatorIdsToProcess.clear();
                initiatorIdsToProcess.addAll(addedInitiators);
            }
        }
        // validate the remove initiator operation against the export mask volumes
        List<URI> volumeURIList = (exportMask.getUserAddedVolumes() != null) ? URIUtil.toURIList(exportMask.getUserAddedVolumes().values()) : new ArrayList<URI>();
        if (volumeURIList.isEmpty()) {
            _log.warn("volume URI list for validating remove initiators is empty...");
        }
        ExportMaskValidationContext ctx = new ExportMaskValidationContext();
        ctx.setStorage(vplex);
        ctx.setExportMask(exportMask);
        ctx.setBlockObjects(volumeURIList, _dbClient);
        ctx.setAllowExceptions(!WorkflowService.getInstance().isStepInRollbackState(stepId));
        validator.removeInitiators(ctx).validate();
        // Invoke artificial failure to simulate invalid storageview name on vplex
        InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_060);
        // removing all storage ports but leaving the existing initiators and volumes.
        if (!exportMask.hasAnyExistingInitiators() && !exportMask.hasAnyExistingVolumes()) {
            if (targetURIs != null && targetURIs.isEmpty() == false) {
                List<PortInfo> targetPortInfos = new ArrayList<PortInfo>();
                List<URI> targetsAddedToStorageView = new ArrayList<URI>();
                for (URI target : targetURIs) {
                    // Do not try to remove a port twice.
                    if (!exportMask.getStoragePorts().contains(target.toString())) {
                        continue;
                    }
                    // Build the PortInfo structure for the port to be added
                    StoragePort port = getDataObject(StoragePort.class, target, _dbClient);
                    PortInfo pi = new PortInfo(port.getPortNetworkId().toUpperCase().replaceAll(":", ""), null, port.getPortName(), null);
                    targetPortInfos.add(pi);
                    targetsAddedToStorageView.add(target);
                }
                if (!targetPortInfos.isEmpty()) {
                    // Remove the targets from the VPLEX
                    client.removeTargetsFromStorageView(exportMask.getMaskName(), targetPortInfos);
                }
            }
        }
        // Update the initiators in the ExportMask.
        List<PortInfo> initiatorPortInfo = new ArrayList<PortInfo>();
        for (URI initiatorURI : initiatorIdsToProcess) {
            Initiator initiator = getDataObject(Initiator.class, initiatorURI, _dbClient);
            // We don't want to remove existing initiator, unless this is a rollback step
            if (exportMask.hasExistingInitiator(initiator) && !WorkflowService.getInstance().isStepInRollbackState(stepId)) {
                continue;
            }
            PortInfo portInfo = new PortInfo(initiator.getInitiatorPort().toUpperCase().replaceAll(":", ""), initiator.getInitiatorNode().toUpperCase().replaceAll(":", ""), initiator.getLabel(), getVPlexInitiatorType(initiator));
            initiatorPortInfo.add(portInfo);
        }
        // Remove the initiators if there aren't any existing volumes, unless this is a rollback step or validation is disabled.
        if (!initiatorPortInfo.isEmpty() && (!exportMask.hasAnyExistingVolumes() || !validatorConfig.isValidationEnabled() || WorkflowService.getInstance().isStepInRollbackState(stepId))) {
            String lockName = null;
            boolean lockAcquired = false;
            try {
                ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
                String clusterId = ConnectivityUtil.getVplexClusterForVarray(exportGroup.getVirtualArray(), vplexURI, _dbClient);
                lockName = _vplexApiLockManager.getLockName(vplexURI, clusterId);
                lockAcquired = _vplexApiLockManager.acquireLock(lockName, LockTimeoutValue.get(LockType.VPLEX_API_LIB));
                if (!lockAcquired) {
                    throw VPlexApiException.exceptions.couldNotObtainConcurrencyLock(vplex.getLabel());
                }
                // Remove the targets from the VPLEX
                // Test mechanism to invoke a failure. No-op on production systems.
                InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_016);
                client.removeInitiatorsFromStorageView(exportMask.getMaskName(), vplexClusterName, initiatorPortInfo);
            } finally {
                if (lockAcquired) {
                    _vplexApiLockManager.releaseLock(lockName);
                }
            }
        }
        completer.ready(_dbClient);
    } catch (VPlexApiException vae) {
        _log.error("Exception removing initiator from Storage View: " + vae.getMessage(), vae);
        failStep(completer, stepId, vae);
    } catch (Exception ex) {
        _log.error("Exception removing initiator from Storage View: " + ex.getMessage(), ex);
        String opName = ResourceOperationTypeEnum.DELETE_STORAGE_VIEW_INITIATOR.getName();
        ServiceError serviceError = VPlexApiException.errors.storageViewRemoveInitiatorFailed(opName, ex);
        failStep(completer, stepId, serviceError);
    }
}
Also used : ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) VPlexStorageViewInfo(com.emc.storageos.vplex.api.VPlexStorageViewInfo) ExportMask(com.emc.storageos.db.client.model.ExportMask) ArrayList(java.util.ArrayList) StoragePort(com.emc.storageos.db.client.model.StoragePort) ExportMaskRemoveInitiatorCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter) NamedURI(com.emc.storageos.db.client.model.NamedURI) URI(java.net.URI) InternalException(com.emc.storageos.svcs.errorhandling.resources.InternalException) InternalServerErrorException(com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) ControllerException(com.emc.storageos.volumecontroller.ControllerException) IOException(java.io.IOException) URISyntaxException(java.net.URISyntaxException) WorkflowException(com.emc.storageos.workflow.WorkflowException) DatabaseException(com.emc.storageos.db.exceptions.DatabaseException) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) PortInfo(com.emc.storageos.vplex.api.clientdata.PortInfo) ExportMaskValidationContext(com.emc.storageos.volumecontroller.impl.validators.contexts.ExportMaskValidationContext) ExportGroup(com.emc.storageos.db.client.model.ExportGroup) Initiator(com.emc.storageos.db.client.model.Initiator) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient) ExportOperationContext(com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext) ExportOperationContextOperation(com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext.ExportOperationContextOperation) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Example 7 with ExportMaskRemoveInitiatorCompleter

use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter in project coprhd-controller by CoprHD.

the class VPlexDeviceController method storageViewRemoveStoragePorts.

/**
 * Workflow Step to remove storage ports from Storage View.
 * Note arguments (except stepId) must match storageViewRemoveStoragePortsMethod above.
 *
 * @param vplexURI
 *            -- URI of VPlex StorageSystem
 * @param exportURI
 *            -- ExportGroup URI
 * @param maskURI
 *            -- ExportMask URI.
 * @param targetURIs
 *            -- list of targets URIs (VPLEX FE ports) to be removed.
 *            If non null, the targets (VPlex front end ports) indicated by the targetURIs will be removed
 *            from the Storage View.
 * @param rollbackContextKey
 *            -- Context token for rollback processing
 * @param stepId
 *            -- Workflow step id.
 * @throws WorkflowException
 */
public void storageViewRemoveStoragePorts(URI vplexURI, URI exportURI, URI maskURI, List<URI> targetURIs, String rollbackContextKey, String stepId) throws DeviceControllerException {
    ExportMaskRemoveInitiatorCompleter completer = null;
    try {
        WorkflowStepCompleter.stepExecuting(stepId);
        completer = new ExportMaskRemoveInitiatorCompleter(exportURI, maskURI, new ArrayList<URI>(), stepId);
        StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
        ExportMask exportMask = _dbClient.queryObject(ExportMask.class, maskURI);
        VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
        String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
        Map<String, String> targetPortMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
        VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
        _log.info("Refreshing ExportMask {}", exportMask.getMaskName());
        VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortMap, _networkDeviceController);
        // get the context from the task completer, in case this is a rollback.
        if (rollbackContextKey != null) {
            ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(rollbackContextKey);
            if (context != null) {
                // a non-null context means this step is running as part of a rollback.
                List<URI> addedTargets = new ArrayList<>();
                if (context.getOperations() != null) {
                    _log.info("Handling storageViewRemoveStoragePorts as a result of rollback");
                    ListIterator<ExportOperationContextOperation> li = context.getOperations().listIterator(context.getOperations().size());
                    while (li.hasPrevious()) {
                        ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
                        if (operation != null && VplexExportOperationContext.OPERATION_ADD_TARGETS_TO_STORAGE_VIEW.equals(operation.getOperation())) {
                            addedTargets = (List<URI>) operation.getArgs().get(0);
                            _log.info(String.format("Removing target port(s) %s from storage view %s as part of rollback", Joiner.on(',').join(addedTargets), exportMask.getMaskName()));
                        }
                    }
                }
                if (addedTargets == null || addedTargets.isEmpty()) {
                    _log.info("There was no context found for add target. So there is nothing to rollback.");
                    completer.ready(_dbClient);
                    return;
                }
                // Change the list of targets to process to the list
                // that successfully were added during addStoragePorts.
                targetURIs.clear();
                targetURIs.addAll(addedTargets);
            }
        }
        // validate the remove storage port operation against the export mask volumes
        // this is conceptually the same as remove initiators, so will validate with volumes
        List<URI> volumeURIList = (exportMask.getUserAddedVolumes() != null) ? URIUtil.toURIList(exportMask.getUserAddedVolumes().values()) : new ArrayList<URI>();
        if (volumeURIList.isEmpty()) {
            _log.warn("volume URI list for validating remove initiators is empty...");
        }
        // removing all storage ports but leaving the existing initiators and volumes.
        if (!exportMask.hasAnyExistingInitiators() && !exportMask.hasAnyExistingVolumes()) {
            ExportMaskValidationContext ctx = new ExportMaskValidationContext();
            ctx.setStorage(vplex);
            ctx.setExportMask(exportMask);
            ctx.setBlockObjects(volumeURIList, _dbClient);
            ctx.setAllowExceptions(!WorkflowService.getInstance().isStepInRollbackState(stepId));
            validator.removeInitiators(ctx).validate();
            if (targetURIs != null && targetURIs.isEmpty() == false) {
                List<PortInfo> targetPortInfos = new ArrayList<PortInfo>();
                List<URI> targetsToRemoveFromStorageView = new ArrayList<URI>();
                for (URI target : targetURIs) {
                    // Do not try to remove a port twice.
                    if (!exportMask.getStoragePorts().contains(target.toString())) {
                        continue;
                    }
                    // Build the PortInfo structure for the port to be added
                    StoragePort port = getDataObject(StoragePort.class, target, _dbClient);
                    PortInfo pi = new PortInfo(port.getPortNetworkId().toUpperCase().replaceAll(":", ""), null, port.getPortName(), null);
                    targetPortInfos.add(pi);
                    targetsToRemoveFromStorageView.add(target);
                }
                if (!targetPortInfos.isEmpty()) {
                    // Remove the targets from the VPLEX
                    client.removeTargetsFromStorageView(exportMask.getMaskName(), targetPortInfos);
                    // Remove the targets to the database.
                    for (URI target : targetsToRemoveFromStorageView) {
                        exportMask.removeTarget(target);
                    }
                    _dbClient.updateObject(exportMask);
                }
            }
        }
        completer.ready(_dbClient);
    } catch (VPlexApiException vae) {
        _log.error("Exception removing storage ports from Storage View: " + vae.getMessage(), vae);
        failStep(completer, stepId, vae);
    } catch (Exception ex) {
        _log.error("Exception removing storage ports from Storage View: " + ex.getMessage(), ex);
        String opName = ResourceOperationTypeEnum.DELETE_STORAGE_VIEW_STORAGEPORTS.getName();
        ServiceError serviceError = VPlexApiException.errors.storageViewRemoveStoragePortFailed(opName, ex);
        failStep(completer, stepId, serviceError);
    }
}
Also used : ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) VPlexStorageViewInfo(com.emc.storageos.vplex.api.VPlexStorageViewInfo) ExportMask(com.emc.storageos.db.client.model.ExportMask) ArrayList(java.util.ArrayList) StoragePort(com.emc.storageos.db.client.model.StoragePort) ExportMaskRemoveInitiatorCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter) NamedURI(com.emc.storageos.db.client.model.NamedURI) URI(java.net.URI) InternalException(com.emc.storageos.svcs.errorhandling.resources.InternalException) InternalServerErrorException(com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) ControllerException(com.emc.storageos.volumecontroller.ControllerException) IOException(java.io.IOException) URISyntaxException(java.net.URISyntaxException) WorkflowException(com.emc.storageos.workflow.WorkflowException) DatabaseException(com.emc.storageos.db.exceptions.DatabaseException) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) PortInfo(com.emc.storageos.vplex.api.clientdata.PortInfo) ExportMaskValidationContext(com.emc.storageos.volumecontroller.impl.validators.contexts.ExportMaskValidationContext) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient) ExportOperationContext(com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext) ExportOperationContextOperation(com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext.ExportOperationContextOperation) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Example 8 with ExportMaskRemoveInitiatorCompleter

use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter in project coprhd-controller by CoprHD.

the class MaskingWorkflowEntryPoints method rollbackExportGroupAddInitiators.

/**
 * Rollback entry point. This is a wrapper around the exportRemoveInitiators
 * operation, which requires that we create a specific completer using the token
 * that's passed in. This token is generated by the rollback processing.
 *
 * @param storageURI
 *            [in] - StorageSystem URI
 * @param exportGroupURI
 *            [in] - ExportGroup URI
 * @param exportMaskURI
 *            [in] - ExportMask URI
 * @param volumeURIs
 *            [in] - Impacted volume URIs
 * @param initiatorURIs
 *            [in] - List of Initiator URIs
 * @param contextKey
 *            [in] - context token
 * @param token
 *            [in] - String token generated by the rollback processing
 * @throws ControllerException
 */
public void rollbackExportGroupAddInitiators(URI storageURI, URI exportGroupURI, URI exportMaskURI, List<URI> volumeURIs, List<URI> initiatorURIs, String contextKey, String token) throws ControllerException {
    ExportTaskCompleter taskCompleter = new ExportMaskRemoveInitiatorCompleter(exportGroupURI, exportMaskURI, initiatorURIs, token);
    try {
        ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(contextKey);
        WorkflowService.getInstance().storeStepData(token, context);
    } catch (ClassCastException e) {
        _log.info("Step {} has stored step data other than ExportOperationContext. Exception: {}", token, e);
    }
    doExportGroupRemoveInitiators(storageURI, exportGroupURI, exportMaskURI, volumeURIs, initiatorURIs, true, taskCompleter, token);
}
Also used : ExportTaskCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportTaskCompleter) ExportOperationContext(com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext) ExportMaskRemoveInitiatorCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter)

Example 9 with ExportMaskRemoveInitiatorCompleter

use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter in project coprhd-controller by CoprHD.

the class VmaxMaskingOrchestrator method exportGroupRemoveInitiators.

@Override
public void exportGroupRemoveInitiators(URI storageURI, URI exportGroupURI, List<URI> initiatorURIs, String token) throws Exception {
    BlockStorageDevice device = getDevice();
    ExportOrchestrationTask taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
    StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
    ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
    StringBuffer errorMessage = new StringBuffer();
    logExportGroup(exportGroup, storageURI);
    try {
        // Set up workflow steps.
        Workflow workflow = _workflowService.getNewWorkflow(MaskingWorkflowEntryPoints.getInstance(), "exportGroupRemoveInitiators", true, token);
        Initiator firstInitiator = _dbClient.queryObject(Initiator.class, initiatorURIs.get(0));
        // No need to validate the orchestrator level validation for vplex/rp. Hence ignoring validation for vplex/rp initiators.
        boolean isValidationNeeded = validatorConfig.isValidationEnabled() && !VPlexControllerUtils.isVplexInitiator(firstInitiator, _dbClient) && !ExportUtils.checkIfInitiatorsForRP(Arrays.asList(firstInitiator));
        _log.info("Orchestration level validation needed : {}", isValidationNeeded);
        InitiatorHelper initiatorHelper = new InitiatorHelper(initiatorURIs).process(exportGroup);
        // Populate a map of volumes on the storage device associated with this ExportGroup
        List<BlockObject> blockObjects = new ArrayList<BlockObject>();
        if (exportGroup != null) {
            for (Map.Entry<String, String> entry : exportGroup.getVolumes().entrySet()) {
                URI boURI = URI.create(entry.getKey());
                BlockObject bo = BlockObject.fetch(_dbClient, boURI);
                if (bo.getStorageController().equals(storageURI)) {
                    blockObjects.add(bo);
                }
            }
        }
        Map<URI, Boolean> initiatorIsPartOfFullListFlags = flagInitiatorsThatArePartOfAFullList(exportGroup, initiatorURIs);
        List<String> initiatorNames = new ArrayList<String>();
        for (URI initiatorURI : initiatorURIs) {
            Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI);
            String normalizedName = Initiator.normalizePort(initiator.getInitiatorPort());
            initiatorNames.add(normalizedName);
        }
        _log.info("Normalized initiator names :{}", initiatorNames);
        device.findExportMasks(storage, initiatorNames, false);
        boolean anyOperationsToDo = false;
        Map<URI, ExportMask> refreshedMasks = new HashMap<URI, ExportMask>();
        if (exportGroup != null && exportGroup.getExportMasks() != null) {
            // There were some exports out there that already have some or all of the
            // initiators that we are attempting to remove. We need to only
            // remove the volumes that the user added to these masks
            Map<String, Set<URI>> matchingExportMaskURIs = getInitiatorToExportMaskMap(exportGroup);
            // This loop will determine a list of volumes to update per export mask
            Map<URI, List<URI>> existingMasksToRemoveInitiator = new HashMap<URI, List<URI>>();
            Map<URI, List<URI>> existingMasksToRemoveVolumes = new HashMap<URI, List<URI>>();
            for (Map.Entry<String, Set<URI>> entry : matchingExportMaskURIs.entrySet()) {
                URI initiatorURI = initiatorHelper.getPortNameToInitiatorURI().get(entry.getKey());
                if (initiatorURI == null || !initiatorURIs.contains(initiatorURI)) {
                    // Entry key points to an initiator that was not passed in the remove request
                    continue;
                }
                Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI);
                // Get a list of the ExportMasks that were matched to the initiator
                // go through the initiators and figure out the proper initiator and volume ramifications
                // to the existing masks.
                List<URI> exportMaskURIs = new ArrayList<URI>();
                exportMaskURIs.addAll(entry.getValue());
                List<ExportMask> masks = _dbClient.queryObject(ExportMask.class, exportMaskURIs);
                _log.info(String.format("initiator %s masks {%s}", initiator.getInitiatorPort(), Joiner.on(',').join(exportMaskURIs)));
                for (ExportMask mask : masks) {
                    if (mask == null || mask.getInactive() || !mask.getStorageDevice().equals(storageURI)) {
                        continue;
                    }
                    if (!refreshedMasks.containsKey(mask.getId())) {
                        // refresh the export mask always
                        mask = device.refreshExportMask(storage, mask);
                        refreshedMasks.put(mask.getId(), mask);
                    }
                    _log.info(String.format("mask %s has initiator %s", mask.getMaskName(), initiator.getInitiatorPort()));
                    /**
                     * If user asked to remove Host from Cluster
                     * 1. Check if the export mask is shared across other export Groups, if not remove the host.
                     * 2. If shared, check whether all the initiators of host is being asked to remove
                     * 3. If yes, check if atleast one of the other shared export Group is EXCLUSIVE
                     * 4. If yes, then remove the shared volumes
                     *
                     * In all other cases, remove the initiators.
                     */
                    List<ExportGroup> otherExportGroups = ExportUtils.getOtherExportGroups(exportGroup, mask, _dbClient);
                    if (!otherExportGroups.isEmpty() && initiatorIsPartOfFullListFlags.get(initiatorURI) && ExportUtils.exportMaskHasBothExclusiveAndSharedVolumes(exportGroup, otherExportGroups, mask)) {
                        if (!exportGroup.forInitiator()) {
                            List<URI> removeVolumesList = existingMasksToRemoveVolumes.get(mask.getId());
                            if (removeVolumesList == null) {
                                removeVolumesList = new ArrayList<URI>();
                                existingMasksToRemoveVolumes.put(mask.getId(), removeVolumesList);
                            }
                            for (String volumeIdStr : exportGroup.getVolumes().keySet()) {
                                URI egVolumeID = URI.create(volumeIdStr);
                                if (mask.getUserAddedVolumes().containsValue(volumeIdStr) && !removeVolumesList.contains(egVolumeID)) {
                                    removeVolumesList.add(egVolumeID);
                                }
                            }
                        } else {
                            // Just a reminder to the world in the case where Initiator is used in this odd situation.
                            _log.info("Removing volumes from an Initiator type export group as part of an initiator removal is not supported.");
                        }
                    } else {
                        _log.info(String.format("We can remove initiator %s from mask %s", initiator.getInitiatorPort(), mask.getMaskName()));
                        List<URI> initiators = existingMasksToRemoveInitiator.get(mask.getId());
                        if (initiators == null) {
                            initiators = new ArrayList<URI>();
                            existingMasksToRemoveInitiator.put(mask.getId(), initiators);
                        }
                        if (!initiators.contains(initiator.getId())) {
                            initiators.add(initiator.getId());
                        }
                    }
                }
            }
            Set<URI> masksGettingRemoved = new HashSet<URI>();
            // In this loop we are trying to remove those initiators that exist
            // on a mask that ViPR created.
            String previousStep = null;
            for (Map.Entry<URI, List<URI>> entry : existingMasksToRemoveInitiator.entrySet()) {
                ExportMask mask = _dbClient.queryObject(ExportMask.class, entry.getKey());
                List<URI> initiatorsToRemove = entry.getValue();
                List<URI> initiatorsToRemoveOnStorage = new ArrayList<URI>();
                for (URI initiatorURI : initiatorsToRemove) {
                    Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI);
                    // COP-28729 - We can allow remove initiator or host if the shared mask doesn't have any existing volumes.
                    // Shared masks will have at least one unmanaged volume.
                    String err = ExportUtils.getExportMasksSharingInitiatorAndHasUnManagedVolumes(_dbClient, initiator, mask, existingMasksToRemoveInitiator.keySet());
                    if (err != null) {
                        errorMessage.append(err);
                    }
                    initiatorsToRemoveOnStorage.add(initiatorURI);
                }
                // CTRL-8846 fix : Compare against all the initiators
                Set<String> allMaskInitiators = ExportUtils.getExportMaskAllInitiatorPorts(mask, _dbClient);
                List<Initiator> removableInitiatorList = _dbClient.queryObject(Initiator.class, initiatorsToRemove);
                List<String> portNames = new ArrayList<>(Collections2.transform(removableInitiatorList, CommonTransformerFunctions.fctnInitiatorToPortName()));
                allMaskInitiators.removeAll(portNames);
                if (allMaskInitiators.isEmpty()) {
                    masksGettingRemoved.add(mask.getId());
                    // For this case, we are attempting to remove all the
                    // initiators in the mask. This means that we will have to delete the
                    // exportGroup
                    _log.info(String.format("mask %s has removed all " + "initiators, mask will be deleted from the array.. ", mask.getMaskName()));
                    List<ExportMask> exportMasks = new ArrayList<ExportMask>();
                    exportMasks.add(mask);
                    previousStep = generateExportMaskDeleteWorkflow(workflow, previousStep, storage, exportGroup, mask, getExpectedVolumes(mask), getExpectedInitiators(mask), null);
                    previousStep = generateZoningDeleteWorkflow(workflow, previousStep, exportGroup, exportMasks);
                    anyOperationsToDo = true;
                } else {
                    _log.info(String.format("mask %s - going to remove the " + "following initiators %s. ", mask.getMaskName(), Joiner.on(',').join(initiatorsToRemove)));
                    Map<URI, List<URI>> maskToInitiatorsMap = new HashMap<URI, List<URI>>();
                    maskToInitiatorsMap.put(mask.getId(), initiatorsToRemove);
                    ExportMaskRemoveInitiatorCompleter exportTaskCompleter = new ExportMaskRemoveInitiatorCompleter(exportGroupURI, mask.getId(), initiatorsToRemove, null);
                    previousStep = generateExportMaskRemoveInitiatorsWorkflow(workflow, previousStep, storage, exportGroup, mask, getExpectedVolumes(mask), initiatorsToRemoveOnStorage, true, exportTaskCompleter);
                    previousStep = generateZoningRemoveInitiatorsWorkflow(workflow, previousStep, exportGroup, maskToInitiatorsMap);
                    anyOperationsToDo = true;
                }
            }
            // for the storage array and ExportGroup.
            for (Map.Entry<URI, List<URI>> entry : existingMasksToRemoveVolumes.entrySet()) {
                if (masksGettingRemoved.contains(entry.getKey())) {
                    _log.info("Mask {} is getting removed, no need to remove volumes from it", entry.getKey().toString());
                    continue;
                }
                ExportMask mask = _dbClient.queryObject(ExportMask.class, entry.getKey());
                List<URI> volumesToRemove = entry.getValue();
                List<URI> initiatorsToRemove = existingMasksToRemoveInitiator.get(mask.getId());
                if (initiatorsToRemove != null) {
                    Set<String> initiatorsInExportMask = ExportUtils.getExportMaskAllInitiatorPorts(mask, _dbClient);
                    List<Initiator> removableInitiatorList = _dbClient.queryObject(Initiator.class, initiatorsToRemove);
                    List<String> portNames = new ArrayList<>(Collections2.transform(removableInitiatorList, CommonTransformerFunctions.fctnInitiatorToPortName()));
                    initiatorsInExportMask.removeAll(portNames);
                    if (!initiatorsInExportMask.isEmpty()) {
                        // There are still some initiators in this ExportMask
                        _log.info(String.format("ExportMask %s would have remaining initiators {%s} that require access to {%s}. " + "Not going to remove any of the volumes", mask.getMaskName(), Joiner.on(',').join(initiatorsInExportMask), Joiner.on(", ").join(volumesToRemove)));
                        continue;
                    }
                }
                Collection<String> volumesToRemoveURIStrings = Collections2.transform(volumesToRemove, CommonTransformerFunctions.FCTN_URI_TO_STRING);
                List<String> exportMaskVolumeURIStrings = new ArrayList<String>(mask.getVolumes().keySet());
                exportMaskVolumeURIStrings.removeAll(volumesToRemoveURIStrings);
                boolean hasExistingVolumes = !CollectionUtils.isEmpty(mask.getExistingVolumes());
                List<? extends BlockObject> boList = BlockObject.fetchAll(_dbClient, volumesToRemove);
                if (!hasExistingVolumes && exportMaskVolumeURIStrings.isEmpty()) {
                    _log.info(String.format("All the volumes (%s) from mask %s will be removed, so will have to remove the whole mask. ", Joiner.on(", ").join(volumesToRemove), mask.getMaskName()));
                    errorMessage.append(String.format("Mask %s would have deleted from array ", mask.forDisplay()));
                    // Order matters! Above this would be any remove initiators that would impact other masking views.
                    // Be sure to always remove anything inside the mask before removing the mask itself.
                    previousStep = generateExportMaskDeleteWorkflow(workflow, previousStep, storage, exportGroup, mask, getExpectedVolumes(mask), getExpectedInitiators(mask), null);
                    previousStep = generateZoningDeleteWorkflow(workflow, previousStep, exportGroup, Arrays.asList(mask));
                    anyOperationsToDo = true;
                } else {
                    ExportTaskCompleter completer = new ExportRemoveVolumesOnAdoptedMaskCompleter(exportGroupURI, mask.getId(), volumesToRemove, token);
                    _log.info(String.format("A subset of volumes will be removed from mask %s: %s. ", mask.getMaskName(), Joiner.on(",").join(volumesToRemove)));
                    errorMessage.append(String.format("A subset of volumes will be removed from mask %s: %s. ", mask.forDisplay(), Joiner.on(", ").join(Collections2.transform(boList, CommonTransformerFunctions.fctnDataObjectToForDisplay()))));
                    List<ExportMask> masks = new ArrayList<ExportMask>();
                    masks.add(mask);
                    previousStep = generateExportMaskRemoveVolumesWorkflow(workflow, previousStep, storage, exportGroup, mask, volumesToRemove, getExpectedInitiators(mask), completer);
                    previousStep = generateZoningRemoveVolumesWorkflow(workflow, previousStep, exportGroup, masks, volumesToRemove);
                    anyOperationsToDo = true;
                }
            }
        }
        _log.warn("Error Message {}", errorMessage);
        if (isValidationNeeded && StringUtils.hasText(errorMessage)) {
            throw DeviceControllerException.exceptions.removeInitiatorValidationError(Joiner.on(", ").join(initiatorNames), storage.getLabel(), errorMessage.toString());
        }
        if (anyOperationsToDo) {
            String successMessage = String.format("Successfully removed exports for initiators on StorageArray %s", storage.getLabel());
            workflow.executePlan(taskCompleter, successMessage);
        } else {
            taskCompleter.ready(_dbClient);
        }
    } catch (Exception ex) {
        _log.error("ExportGroup remove initiator Orchestration failed.", ex);
        if (taskCompleter != null) {
            ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(ex.getMessage(), ex);
            taskCompleter.error(_dbClient, serviceError);
        }
    }
}
Also used : ExportTaskCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportTaskCompleter) Set(java.util.Set) HashSet(java.util.HashSet) StringSet(com.emc.storageos.db.client.model.StringSet) HashMap(java.util.HashMap) Lists.newArrayList(com.google.common.collect.Lists.newArrayList) ArrayList(java.util.ArrayList) URI(java.net.URI) ExportMaskRemoveInitiatorCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter) BlockStorageDevice(com.emc.storageos.volumecontroller.BlockStorageDevice) Initiator(com.emc.storageos.db.client.model.Initiator) List(java.util.List) Lists.newArrayList(com.google.common.collect.Lists.newArrayList) ArrayList(java.util.ArrayList) URIQueryResultList(com.emc.storageos.db.client.constraint.URIQueryResultList) BlockObject(com.emc.storageos.db.client.model.BlockObject) StorageSystem(com.emc.storageos.db.client.model.StorageSystem) HashSet(java.util.HashSet) ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) ExportMask(com.emc.storageos.db.client.model.ExportMask) Workflow(com.emc.storageos.workflow.Workflow) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) ExportRemoveVolumesOnAdoptedMaskCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportRemoveVolumesOnAdoptedMaskCompleter) ExportGroup(com.emc.storageos.db.client.model.ExportGroup) Map(java.util.Map) HashMap(java.util.HashMap) StringMap(com.emc.storageos.db.client.model.StringMap) ExportOrchestrationTask(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportOrchestrationTask)

Aggregations

ExportMaskRemoveInitiatorCompleter (com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskRemoveInitiatorCompleter)9 ExportMask (com.emc.storageos.db.client.model.ExportMask)6 DeviceControllerException (com.emc.storageos.exceptions.DeviceControllerException)6 ExportOperationContext (com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext)6 URI (java.net.URI)6 ArrayList (java.util.ArrayList)6 Initiator (com.emc.storageos.db.client.model.Initiator)5 ServiceError (com.emc.storageos.svcs.errorhandling.model.ServiceError)5 ExportOperationContextOperation (com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext.ExportOperationContextOperation)5 ExportTaskCompleter (com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportTaskCompleter)4 ExportMaskValidationContext (com.emc.storageos.volumecontroller.impl.validators.contexts.ExportMaskValidationContext)4 StorageSystem (com.emc.storageos.db.client.model.StorageSystem)3 HashSet (java.util.HashSet)3 ListIterator (java.util.ListIterator)3 URIQueryResultList (com.emc.storageos.db.client.constraint.URIQueryResultList)2 ExportGroup (com.emc.storageos.db.client.model.ExportGroup)2 NamedURI (com.emc.storageos.db.client.model.NamedURI)2 StoragePort (com.emc.storageos.db.client.model.StoragePort)2 StringSet (com.emc.storageos.db.client.model.StringSet)2 DatabaseException (com.emc.storageos.db.exceptions.DatabaseException)2