Search in sources :

Example 31 with BlockStorageDevice

use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.

the class VPlexBackendManager method verifyExportMaskOnSystem.

/**
 * Verify that an ExportMask that is going to be used is on the StorageSystem.
 *
 * @param mask
 * @param array
 */
private void verifyExportMaskOnSystem(ExportMask mask, StorageSystem array) {
    VplexBackEndMaskingOrchestrator maskingOrchestrator = getOrch(array);
    BlockStorageDevice storageDevice = _blockDeviceController.getDevice(array.getSystemType());
    // Make a list of Initiators used by the ExportMask. These could be initiators
    // explicitly named in the Export Mask, or Initiators that match addresses in the existingInitiators
    // fields. (The later occurs for externally created ExportMasks.)
    List<Initiator> initiators = new ArrayList<Initiator>();
    initiators.addAll(ExportMaskUtils.getInitiatorsForExportMask(_dbClient, mask, Transport.FC));
    if (initiators.isEmpty()) {
        initiators.addAll(ExportMaskUtils.getExistingInitiatorsForExportMask(_dbClient, mask, Transport.FC));
    }
    Map<URI, ExportMask> maskSet = maskingOrchestrator.readExistingExportMasks(array, storageDevice, initiators);
    if (maskSet.containsKey(mask.getId())) {
        _log.info(String.format("Verified ExportMask %s present on %s", mask.getMaskName(), array.getNativeGuid()));
        return;
    }
}
Also used : BlockStorageDevice(com.emc.storageos.volumecontroller.BlockStorageDevice) Initiator(com.emc.storageos.db.client.model.Initiator) VplexBackEndMaskingOrchestrator(com.emc.storageos.volumecontroller.impl.block.VplexBackEndMaskingOrchestrator) ExportMask(com.emc.storageos.db.client.model.ExportMask) ArrayList(java.util.ArrayList) URI(java.net.URI)

Example 32 with BlockStorageDevice

use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.

the class VPlexBackendManager method addWorkflowStepsToRemoveBackendVolumes.

/**
 * Remove a list of volumes from the ExportGroup specified.
 *
 * @param workflow
 *            = Workflow steps are to be added to
 * @param waitFor
 *            - Wait for completion of this workflow step
 * @param storage
 *            - Storage SclusterUnknownystem
 * @param exportGroupURI-
 *            Export Group to be processed
 * @param blockObjectList
 *            - list of volumes or snapshot (URIs)
 * @return true if any steps added to Workflow
 * @throws DeviceControllerException
 */
public boolean addWorkflowStepsToRemoveBackendVolumes(Workflow workflow, String waitFor, StorageSystem storage, URI exportGroupURI, List<URI> blockObjectList) throws DeviceControllerException {
    ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
    boolean stepsAdded = false;
    // Read all the ExportMasks
    Map<String, ExportMask> exportMasks = new HashMap<String, ExportMask>();
    Map<String, List<URI>> maskToVolumes = new HashMap<String, List<URI>>();
    List<ExportMask> egExportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup);
    for (ExportMask mask : egExportMasks) {
        if (mask == null || mask.getInactive()) {
            continue;
        }
        exportMasks.put(mask.getId().toString(), mask);
        maskToVolumes.put(mask.getId().toString(), new ArrayList<URI>());
    }
    // Put this information in the maskToVolumes map.
    for (URI blockObjectURI : blockObjectList) {
        for (ExportMask mask : exportMasks.values()) {
            if (mask.hasVolume(blockObjectURI)) {
                maskToVolumes.get(mask.getId().toString()).add(blockObjectURI);
            } else {
                _log.info(String.format("ExportMask %s (%s) does not contain volume %s", mask.getMaskName(), mask.getId(), blockObjectURI));
            }
        }
    }
    // Now process each Export Mask.
    // refresh export masks per XIO storage array
    boolean needRefresh = storage.deviceIsType(Type.xtremio);
    String previousStepId = waitFor;
    for (ExportMask mask : exportMasks.values()) {
        List<URI> volumes = maskToVolumes.get(mask.getId().toString());
        if (volumes.isEmpty()) {
            _log.info("No volumes to remove for Export Mask: " + mask.getId());
            continue;
        }
        previousStepId = waitFor;
        // Verify the ExportMask is present on the system, or check if it was renamed
        verifyExportMaskOnSystem(mask, storage);
        if (mask.getCreatedBySystem()) {
            _log.info(String.format("Generating unzoning step for ExportMask %s", mask.getMaskName()));
            // Since this mask was created by the system, we want to unzone it.
            List<URI> maskURIs = Collections.singletonList(mask.getId());
            List<NetworkZoningParam> zoningParams = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), maskURIs, _dbClient);
            Workflow.Method zoneRemoveMethod = _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParams, volumes);
            previousStepId = workflow.createStep(ZONING_STEP, String.format("Removing zones for ExportMask %s", mask.getMaskName()), previousStepId, nullURI, "network-system", _networkDeviceController.getClass(), zoneRemoveMethod, zoneRemoveMethod, null);
        } else {
            _log.info(String.format("ExportMask %s not created by ViPR; no unzoning step", mask.getMaskName()));
        }
        VplexBackEndMaskingOrchestrator orca = getOrch(storage);
        List<URI> initiatorURIs = new ArrayList<>();
        if (mask.getInitiators() != null) {
            initiatorURIs = new ArrayList<URI>(Collections2.transform(mask.getInitiators(), CommonTransformerFunctions.FCTN_STRING_TO_URI));
        }
        if (needRefresh) {
            BlockStorageDevice device = _blockDeviceController.getDevice(storage.getSystemType());
            device.refreshExportMask(storage, mask);
            needRefresh = false;
        }
        Workflow.Method removeVolumesMethod = orca.deleteOrRemoveVolumesFromExportMaskMethod(storage.getId(), exportGroup.getId(), mask.getId(), volumes, initiatorURIs);
        String stepId = workflow.createStepId();
        workflow.createStep(EXPORT_STEP, String.format("Removing volume from ExportMask %s", mask.getMaskName()), previousStepId, storage.getId(), storage.getSystemType(), orca.getClass(), removeVolumesMethod, removeVolumesMethod, stepId);
        _log.info(String.format("Generated remove volume from ExportMask %s for volumes %s", mask.getMaskName(), volumes));
        stepsAdded = true;
    }
    return stepsAdded;
}
Also used : HashMap(java.util.HashMap) ExportMask(com.emc.storageos.db.client.model.ExportMask) ArrayList(java.util.ArrayList) Workflow(com.emc.storageos.workflow.Workflow) URI(java.net.URI) NetworkZoningParam(com.emc.storageos.networkcontroller.impl.NetworkZoningParam) ExportGroup(com.emc.storageos.db.client.model.ExportGroup) BlockStorageDevice(com.emc.storageos.volumecontroller.BlockStorageDevice) VplexBackEndMaskingOrchestrator(com.emc.storageos.volumecontroller.impl.block.VplexBackEndMaskingOrchestrator) List(java.util.List) ArrayList(java.util.ArrayList) URIQueryResultList(com.emc.storageos.db.client.constraint.URIQueryResultList)

Example 33 with BlockStorageDevice

use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.

the class VPlexBackendManager method chooseBackendExportMask.

/**
 * Choose one of the existing Export Masks (on VMAX: masking views) if possible in
 * which to place the volume to be exported to the VPlex. Otherwise ExportMask(s)
 * will be generated and one will be chosen from the generated set.
 *
 * @param vplex
 *            [IN] - VPlex storage system
 * @param array
 *            [IN] - Storage Array storage system
 * @param varrayURI
 *            [IN] - Virtual array
 * @param volumeMap
 *            [IN] - Map of URI to their corresponding Volume object
 * @param stepId
 *            the workflow step id used find the workflow where the existing zone information is stored
 * @return ExportMaskPlacementDescriptor - data structure that will indicate the mapping of ExportMasks to
 *         ExportGroups and ExportMasks to Volumes.
 * @throws ControllerException
 */
public ExportMaskPlacementDescriptor chooseBackendExportMask(StorageSystem vplex, StorageSystem array, URI varrayURI, Map<URI, Volume> volumeMap, String stepId) throws ControllerException {
    _log.info(String.format("Searching for existing ExportMasks between Vplex %s (%s) and Array %s (%s) in Varray %s", vplex.getLabel(), vplex.getNativeGuid(), array.getLabel(), array.getNativeGuid(), varrayURI));
    long startTime = System.currentTimeMillis();
    // The volumeMap can contain volumes from different arrays. We are interested only in the ones for 'array'.
    Map<URI, Volume> volumesForArray = filterVolumeMap(volumeMap, array);
    // Build the data structures used for analysis and validation.
    buildDataStructures(vplex, array, varrayURI);
    VplexBackEndMaskingOrchestrator vplexBackendOrchestrator = getOrch(array);
    BlockStorageDevice storageDevice = _blockDeviceController.getDevice(array.getSystemType());
    // Lock operation.
    String lockName = _vplexApiLockManager.getLockName(vplex.getId(), _cluster, array.getId());
    boolean lockAcquired = false;
    try {
        if (_vplexApiLockManager != null) {
            lockAcquired = _vplexApiLockManager.acquireLock(lockName, MAX_LOCK_WAIT_SECONDS);
            if (!lockAcquired) {
                _log.info("Timed out waiting on lock- PROCEEDING ANYWAY!");
            }
        }
        // Initialize the placement data structure
        ExportMaskPlacementDescriptor placementDescriptor = ExportMaskPlacementDescriptor.create(_tenantURI, _projectURI, vplex, array, varrayURI, volumesForArray, _idToInitiatorMap.values());
        // VplexBackEndMaskingOrchestrator#suggestExportMasksForPlacement should fill in the rest of the
        // placement data structures, such that decisions on how to reuse the ExportMasks can be done here.
        // At a minimum, this placement is done based on reading the ExportMasks off the backend array based on the
        // initiators.
        // Customizations can be done per array based on other factors. Most notably, for the case of VMAX, this
        // would
        // place volumes in appropriate ExportMasks based on the volume's AutoTieringPolicy relationship.
        vplexBackendOrchestrator.suggestExportMasksForPlacement(array, storageDevice, _initiators, placementDescriptor);
        // Apply the filters that will remove any ExportMasks that do not fit the expected VPlex masking paradigm
        Set<URI> invalidMasks = filterExportMasksByVPlexRequirements(vplex, array, varrayURI, placementDescriptor);
        // If there were any invalid masks found, we can redo the volume placement into
        // an alternative ExportMask (if there are any listed by the descriptor)
        putUnplacedVolumesIntoAlternativeMask(placementDescriptor);
        // If not, we will attempt to generate some.
        if (!placementDescriptor.hasMasks()) {
            _log.info("There weren't any ExportMasks in the placementDescriptor. Creating new ExportMasks for the volumes.");
            // Did not find any reusable ExportMasks. Either there were some that matched initiators, but did not
            // meeting the
            // VPlex criteria, or there were no existing masks for the backend at all.
            Map<URI, Volume> volumesToPlace = placementDescriptor.getVolumesToPlace();
            createVPlexBackendExportMasksForVolumes(vplex, array, varrayURI, placementDescriptor, invalidMasks, volumesToPlace, stepId);
        } else if (placementDescriptor.hasUnPlacedVolumes()) {
            _log.info("There were some reusable ExportMasks found, but not all volumes got placed. Will create an ExportMask to " + "hold these unplaced volumes.");
            // There were some matching ExportMasks found on the backend array, but we also have some
            // unplaced
            // volumes. We need to create new ExportMasks to hold these unplaced volumes.
            // We will leave the placement hint to whatever was determined by the suggestExportMasksForPlacement
            // call
            Map<URI, Volume> unplacedVolumes = placementDescriptor.getUnplacedVolumes();
            createVPlexBackendExportMasksForVolumes(vplex, array, varrayURI, placementDescriptor, invalidMasks, unplacedVolumes, stepId);
        }
        // At this point, we have:
        // 
        // a). Requested that the backend StorageArray provide us with a list of ExportMasks that can support the
        // initiators + volumes.
        // b). Processed the suggested ExportMasks in case they had their names changed
        // c). Filtered out any ExportMasks that do not fit the VPlex masking paradigm
        // OR
        // d). Created a set of new ExportMasks to support the initiators + volumes
        // 
        // We will now run the final placement based on a strategy determined by looking at the placementDescriptor
        VPlexBackendPlacementStrategyFactory.create(_dbClient, placementDescriptor).execute();
        long elapsed = System.currentTimeMillis() - startTime;
        _log.info(String.format("PlacementDescriptor processing took %f seconds", (double) elapsed / (double) 1000));
        _log.info(String.format("PlacementDescriptor was created:%n%s", placementDescriptor.toString()));
        return placementDescriptor;
    } finally {
        if (lockAcquired) {
            _vplexApiLockManager.releaseLock(lockName);
        }
    }
}
Also used : BlockStorageDevice(com.emc.storageos.volumecontroller.BlockStorageDevice) Volume(com.emc.storageos.db.client.model.Volume) VplexBackEndMaskingOrchestrator(com.emc.storageos.volumecontroller.impl.block.VplexBackEndMaskingOrchestrator) ExportMaskPlacementDescriptor(com.emc.storageos.volumecontroller.impl.block.ExportMaskPlacementDescriptor) URI(java.net.URI) Map(java.util.Map) HashMap(java.util.HashMap) StringSetMap(com.emc.storageos.db.client.model.StringSetMap)

Example 34 with BlockStorageDevice

use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.

the class VPlexDeviceController method createWorkflowStepsForBlockVolumeExport.

/**
 * Create workflow steps in the passed workflow to create export groups for the
 * front end ports of the passed storage systems to the backend ports of the
 * passed VPlex storage system for the purpose of exposing volumes on these
 * storage system to the VPlex.
 *
 * @param workflow
 *            The workflow to which the export steps are added.
 * @param vplexSystem
 *            A reference to the VPlex storage system.
 * @param storageSystemMap
 *            The list of storage systems to export.
 * @param volumeMap
 *            The volumes to be exported.
 * @param projectURI
 *            The project reference.
 * @param tenantURI
 *            The tenant reference.
 * @param dependantStepId
 *            The dependent step if, typically a volume creation step.
 *
 * @throws IOException
 *             When an error occurs.
 * @throws ControllerException
 */
private String createWorkflowStepsForBlockVolumeExport(Workflow workflow, StorageSystem vplexSystem, Map<URI, StorageSystem> storageSystemMap, Map<URI, Volume> volumeMap, URI projectURI, URI tenantURI, String dependantStepId) throws IOException, ControllerException {
    String lastStep = dependantStepId;
    // The following adds a rollback step to forget the volumes if something
    // goes wrong. This is a nop on provisioning.
    lastStep = addRollbackStepToForgetVolumes(workflow, vplexSystem.getId(), new ArrayList<URI>(volumeMap.keySet()), lastStep);
    String forgetRBStep = lastStep;
    URI vplexURI = vplexSystem.getId();
    // Populate the container for the export workflow step generation
    for (Map.Entry<URI, StorageSystem> storageEntry : storageSystemMap.entrySet()) {
        URI storageSystemURI = storageEntry.getKey();
        StorageSystem storageSystem = storageEntry.getValue();
        URI varray = getVolumesVarray(storageSystem, volumeMap.values());
        _log.info(String.format("Creating ExportGroup for storage system %s (%s) in Virtual Aarray[(%s)]", storageSystem.getLabel(), storageSystemURI, varray));
        if (varray == null) {
            // For whatever reason, there were no Volumes for this Storage System found, so we
            // definitely do not want to create anything. Log a warning and continue.
            _log.warn(String.format("No Volumes for storage system %s (%s), no need to create an ExportGroup.", storageSystem.getLabel(), storageSystemURI));
            continue;
        }
        // Get the initiator port map for this VPLEX and storage system
        // for this volumes virtual array.
        VPlexBackendManager backendMgr = new VPlexBackendManager(_dbClient, this, _blockDeviceController, _blockScheduler, _networkDeviceController, projectURI, tenantURI, _vplexApiLockManager, coordinator);
        Map<URI, List<StoragePort>> initiatorPortMap = backendMgr.getInitiatorPortsForArray(vplexURI, storageSystemURI, varray);
        // If there are no networks that can be zoned, error.
        if (initiatorPortMap.isEmpty()) {
            throw DeviceControllerException.exceptions.noNetworksConnectingVPlexToArray(vplexSystem.getNativeGuid(), storageSystem.getNativeGuid());
        }
        // Select from an existing ExportMask if possible
        ExportMaskPlacementDescriptor descriptor = backendMgr.chooseBackendExportMask(vplexSystem, storageSystem, varray, volumeMap, lastStep);
        // refresh export masks per XIO storage array
        boolean needRefresh = storageSystem.deviceIsType(DiscoveredDataObject.Type.xtremio);
        // For every ExportMask in the descriptor ...
        for (URI exportMaskURI : descriptor.getPlacedMasks()) {
            // Create steps to place each set of volumes into its assigned ExportMask
            ExportGroup exportGroup = descriptor.getExportGroupForMask(exportMaskURI);
            ExportMask exportMask = descriptor.getExportMask(exportMaskURI);
            Map<URI, Volume> placedVolumes = descriptor.getPlacedVolumes(exportMaskURI);
            if (needRefresh) {
                BlockStorageDevice device = _blockDeviceController.getDevice(storageSystem.getSystemType());
                device.refreshExportMask(storageSystem, exportMask);
                needRefresh = false;
                ;
            }
            // Add the workflow steps.
            lastStep = backendMgr.addWorkflowStepsToAddBackendVolumes(workflow, lastStep, exportGroup, exportMask, placedVolumes, varray, vplexSystem, storageSystem, forgetRBStep);
        }
    }
    return lastStep;
}
Also used : ExportMask(com.emc.storageos.db.client.model.ExportMask) ArrayList(java.util.ArrayList) NamedURI(com.emc.storageos.db.client.model.NamedURI) URI(java.net.URI) ExportGroup(com.emc.storageos.db.client.model.ExportGroup) BlockStorageDevice(com.emc.storageos.volumecontroller.BlockStorageDevice) Volume(com.emc.storageos.db.client.model.Volume) ExportMaskPlacementDescriptor(com.emc.storageos.volumecontroller.impl.block.ExportMaskPlacementDescriptor) ApplicationAddVolumeList(com.emc.storageos.volumecontroller.ApplicationAddVolumeList) ArrayList(java.util.ArrayList) URIQueryResultList(com.emc.storageos.db.client.constraint.URIQueryResultList) List(java.util.List) Map(java.util.Map) OpStatusMap(com.emc.storageos.db.client.model.OpStatusMap) HashMap(java.util.HashMap) StringMap(com.emc.storageos.db.client.model.StringMap) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Example 35 with BlockStorageDevice

use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.

the class VNXeMaskingOrchestrator method exportGroupCreate.

/**
 * Create storage level masking components to support the requested
 * ExportGroup object. ExportMask will be created for each host.
 *
 * @param storageURI
 *            - URI referencing underlying storage array
 * @param exportGroupURI
 *            - URI referencing Bourne-level masking, ExportGroup
 * @param initiatorURIs
 *            - List of Initiator URIs
 * @param volumeMap
 *            - Map of Volume URIs to requested Integer URI
 * @param token
 *            - Identifier for operation
 * @throws Exception
 */
@Override
public void exportGroupCreate(URI storageURI, URI exportGroupURI, List<URI> initiatorURIs, Map<URI, Integer> volumeMap, String token) throws Exception {
    ExportOrchestrationTask taskCompleter = null;
    try {
        BlockStorageDevice device = getDevice();
        ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
        StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
        taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
        if (initiatorURIs != null && !initiatorURIs.isEmpty()) {
            _log.info("export_create: initiator list non-empty");
            // Set up workflow steps.
            Workflow workflow = _workflowService.getNewWorkflow(MaskingWorkflowEntryPoints.getInstance(), "exportGroupCreate", true, token);
            // Create two steps, one for Zoning, one for the ExportGroup actions.
            // This step is for zoning. It is not specific to a single
            // NetworkSystem, as it will look at all the initiators and targets and compute
            // the zones required (which might be on multiple NetworkSystems.)
            boolean createdSteps = determineExportGroupCreateSteps(workflow, null, device, storage, exportGroup, initiatorURIs, volumeMap, token);
            String zoningStep = generateZoningCreateWorkflow(workflow, EXPORT_GROUP_MASKING_TASK, exportGroup, null, volumeMap);
            if (createdSteps) {
                // Execute the plan and allow the WorkflowExecutor to fire the
                // taskCompleter.
                String successMessage = String.format("ExportGroup successfully applied for StorageArray %s", storage.getLabel());
                workflow.executePlan(taskCompleter, successMessage);
            }
        } else {
            _log.info("export_create: initiator list");
            taskCompleter.ready(_dbClient);
        }
    } catch (Exception ex) {
        _log.error("ExportGroup Orchestration failed.", ex);
        // TODO add service code here
        if (taskCompleter != null) {
            ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(ex.getMessage(), ex);
            taskCompleter.error(_dbClient, serviceError);
        }
    }
}
Also used : ExportGroup(com.emc.storageos.db.client.model.ExportGroup) ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) BlockStorageDevice(com.emc.storageos.volumecontroller.BlockStorageDevice) Workflow(com.emc.storageos.workflow.Workflow) ExportOrchestrationTask(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportOrchestrationTask) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Aggregations

BlockStorageDevice (com.emc.storageos.volumecontroller.BlockStorageDevice)49 StorageSystem (com.emc.storageos.db.client.model.StorageSystem)36 URI (java.net.URI)29 ArrayList (java.util.ArrayList)28 ExportMask (com.emc.storageos.db.client.model.ExportMask)27 Initiator (com.emc.storageos.db.client.model.Initiator)23 ServiceError (com.emc.storageos.svcs.errorhandling.model.ServiceError)19 ExportGroup (com.emc.storageos.db.client.model.ExportGroup)17 HashSet (java.util.HashSet)17 ExportOrchestrationTask (com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportOrchestrationTask)16 Workflow (com.emc.storageos.workflow.Workflow)15 StringMap (com.emc.storageos.db.client.model.StringMap)14 DeviceControllerException (com.emc.storageos.exceptions.DeviceControllerException)14 VPlexApiException (com.emc.storageos.vplex.api.VPlexApiException)14 HashMap (java.util.HashMap)11 List (java.util.List)11 ExportTaskCompleter (com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportTaskCompleter)10 BlockObject (com.emc.storageos.db.client.model.BlockObject)8 Map (java.util.Map)8 Set (java.util.Set)8