Search in sources :

Example 6 with ExportPathParams

use of com.emc.storageos.db.client.model.ExportPathParams in project coprhd-controller by CoprHD.

the class BlockIngestExportOrchestrator method updateExportMaskWithPortGroup.

/**
 * Update the ingested exportMask with port group info. If the port group is not in the DB yet, create it.
 *
 * @param system - The storage system the export mask belongs to
 * @param unmanagedMask - The corresponding unmanaged export mask
 * @param mask - The ingested export mask
 * @param exportGroup - The export group that to be updated
 * @param blockId - The block object Id that is being ingested.
 */
protected void updateExportMaskWithPortGroup(StorageSystem system, UnManagedExportMask unmanagedMask, ExportMask mask, ExportGroup exportGroup, URI blockId) {
    boolean portGroupEnabled = false;
    if (Type.vmax.name().equals(system.getSystemType())) {
        portGroupEnabled = Boolean.valueOf(_customConfigHandler.getComputedCustomConfigValue(CustomConfigConstants.VMAX_USE_PORT_GROUP_ENABLED, system.getSystemType(), null));
    }
    // Set port group
    String portGroupName = unmanagedMask.getPortGroup();
    if (NullColumnValueGetter.isNotNullValue(portGroupName)) {
        // Port group name is set in the UnManagedMask
        String guid = String.format("%s+%s", system.getNativeGuid(), portGroupName);
        URIQueryResultList result = new URIQueryResultList();
        _dbClient.queryByConstraint(AlternateIdConstraint.Factory.getPortGroupNativeGuidConstraint(guid), result);
        Iterator<URI> it = result.iterator();
        boolean foundPG = it.hasNext();
        StoragePortGroup portGroup = null;
        if (!foundPG) {
            portGroup = new StoragePortGroup();
            portGroup.setId(URIUtil.createId(StoragePortGroup.class));
            portGroup.setLabel(portGroupName);
            portGroup.setNativeGuid(guid);
            portGroup.setStorageDevice(system.getId());
            portGroup.setInactive(false);
            _dbClient.createObject(portGroup);
        } else {
            URI pgURI = it.next();
            portGroup = _dbClient.queryObject(StoragePortGroup.class, pgURI);
        }
        List<URI> targets = new ArrayList<URI>(Collections2.transform(unmanagedMask.getKnownStoragePortUris(), CommonTransformerFunctions.FCTN_STRING_TO_URI));
        if (portGroup.getStoragePorts() != null && !portGroup.getStoragePorts().isEmpty()) {
            portGroup.getStoragePorts().replace(StringSetUtil.uriListToStringSet(targets));
        } else {
            portGroup.setStoragePorts(StringSetUtil.uriListToStringSet(targets));
        }
        if (portGroupEnabled) {
            portGroup.setRegistrationStatus(RegistrationStatus.REGISTERED.name());
            portGroup.setMutable(false);
        } else {
            portGroup.setRegistrationStatus(RegistrationStatus.UNREGISTERED.name());
            portGroup.setMutable(true);
        }
        _dbClient.updateObject(portGroup);
        mask.setPortGroup(portGroup.getId());
        _dbClient.updateObject(mask);
        // Update export group pathParms if port group feature enabled.
        if (portGroupEnabled && blockId != null) {
            ExportPathParams pathParam = new ExportPathParams();
            pathParam.setLabel(exportGroup.getLabel());
            pathParam.setExplicitlyCreated(false);
            pathParam.setId(URIUtil.createId(ExportPathParams.class));
            pathParam.setPortGroup(portGroup.getId());
            pathParam.setInactive(false);
            _dbClient.createObject(pathParam);
            exportGroup.addToPathParameters(blockId, pathParam.getId());
            _dbClient.updateObject(exportGroup);
        }
    }
}
Also used : StoragePortGroup(com.emc.storageos.db.client.model.StoragePortGroup) ArrayList(java.util.ArrayList) URI(java.net.URI) URIQueryResultList(com.emc.storageos.db.client.constraint.URIQueryResultList) ExportPathParams(com.emc.storageos.db.client.model.ExportPathParams)

Example 7 with ExportPathParams

use of com.emc.storageos.db.client.model.ExportPathParams in project coprhd-controller by CoprHD.

the class VolumeIngestionUtil method verifyNumPath.

/**
 * Given a ZoneInfoMap, check that the hosts in a cluster have a number of
 * paths that is compliant with the vpool specifications.
 *
 * @param initiatorUris
 *            a list of initiators sets, each set belongs to one host in the
 *            cluster
 * @param umask
 *            the UnManagedExportMask being checked
 * @param block
 *            the volume or snapshot for which the zoning are verified
 * @param vPoolURI
 *            - URI of the VPool to ingest blockObject.
 * @param varrayURI
 *            - URI of the Varray to ingest blockObject.
 * @param dbClient
 *            an instance of dbclient
 * @return true if the number of paths is valid.
 */
private static boolean verifyNumPath(List<Set<String>> initiatorUris, UnManagedExportMask umask, BlockObject block, URI vPoolURI, URI varrayUri, DbClient dbClient) {
    DbModelClientImpl dbModelClient = new DbModelClientImpl(dbClient);
    ExportPathParams pathParams = BlockStorageScheduler.getExportPathParam(block, vPoolURI, dbClient);
    for (Set<String> hostInitiatorUris : initiatorUris) {
        List<Initiator> initiators = CustomQueryUtility.iteratorToList(dbModelClient.find(Initiator.class, StringSetUtil.stringSetToUriList(hostInitiatorUris)));
        // If this an RP initiator, do not validate num path against the vpool; it's a back-end mask with different
        // pathing rules.
        boolean avoidNumPathCheck = false;
        for (Initiator initiator : initiators) {
            if (initiator.checkInternalFlags(Flag.RECOVERPOINT)) {
                avoidNumPathCheck = true;
            }
        }
        // if vplex distributed, only verify initiators connected to same vplex cluster as unmanaged export mask
        if (isVplexVolume(block, dbClient)) {
            VirtualPool vpool = dbClient.queryObject(VirtualPool.class, vPoolURI);
            if (VirtualPool.vPoolSpecifiesHighAvailabilityDistributed(vpool)) {
                _logger.info("initiators before filtering for vplex distributed: " + initiators);
                // determine the source and ha vplex cluster names for comparing to the unmanaged export mask
                StorageSystem vplex = dbClient.queryObject(StorageSystem.class, umask.getStorageSystemUri());
                String sourceVarrayVplexClusterName = VPlexControllerUtils.getVPlexClusterName(dbClient, varrayUri, vplex.getId());
                List<URI> varrayUris = new ArrayList<URI>();
                varrayUris.add(varrayUri);
                URI haVarrayUri = VPlexUtil.getHAVarray(vpool);
                String haVarrayVplexClusterName = null;
                if (null != haVarrayUri) {
                    varrayUris.add(haVarrayUri);
                    haVarrayVplexClusterName = VPlexControllerUtils.getVPlexClusterName(dbClient, haVarrayUri, vplex.getId());
                }
                // determine the vplex cluster name that the unmanaged export mask resides upon
                String umaskVplexClusterId = ConnectivityUtil.getVplexClusterForStoragePortUris(URIUtil.toURIList(umask.getKnownStoragePortUris()), umask.getStorageSystemUri(), dbClient);
                String umaskVplexClusterName = VPlexControllerUtils.getClusterNameForId(umaskVplexClusterId, vplex.getId(), dbClient);
                // partition the host's initiators by virtual array (source or high availability)
                Map<URI, List<URI>> varraysToInitiators = VPlexUtil.partitionInitiatorsByVarray(dbClient, URIUtil.toURIList(hostInitiatorUris), varrayUris, vplex);
                // determine the varray to check by matching the vplex cluster names
                URI varrayToCheck = null;
                URI otherVarray = null;
                if (null != umaskVplexClusterName) {
                    if (umaskVplexClusterName.equalsIgnoreCase(sourceVarrayVplexClusterName)) {
                        varrayToCheck = varrayUri;
                        otherVarray = haVarrayUri;
                    } else if (umaskVplexClusterName.equalsIgnoreCase(haVarrayVplexClusterName)) {
                        varrayToCheck = haVarrayUri;
                        otherVarray = varrayUri;
                    }
                } else {
                    _logger.error("Could not determine UnManagedExportMask VPLEX cluster name for mask " + umask.getMaskName());
                    return false;
                }
                // if no initiators match, then skip the num path check, it doesn't apply to this host.
                if (null != varrayToCheck) {
                    List<URI> initsToCheck = varraysToInitiators.get(varrayToCheck);
                    if (initsToCheck != null && !initsToCheck.isEmpty()) {
                        initiators = CustomQueryUtility.iteratorToList(dbModelClient.find(Initiator.class, initsToCheck));
                    } else {
                        List<URI> otherVarrayInits = varraysToInitiators.get(otherVarray);
                        if (null != otherVarrayInits && !otherVarrayInits.isEmpty()) {
                            avoidNumPathCheck = true;
                        }
                    }
                } else {
                    _logger.error("inits not filtered for vplex distributed because a varray couldn't be determined for mask " + umask.getMaskName());
                    return false;
                }
                _logger.info("initiators after filtering for vplex distributed: " + initiators);
            }
        }
        if (hasFCInitiators(initiators) && !avoidNumPathCheck) {
            return verifyHostNumPath(pathParams, initiators, umask.getZoningMap(), dbClient);
        }
    }
    return true;
}
Also used : ArrayList(java.util.ArrayList) VirtualPool(com.emc.storageos.db.client.model.VirtualPool) NamedURI(com.emc.storageos.db.client.model.NamedURI) URI(java.net.URI) DbModelClientImpl(com.emc.storageos.db.client.impl.DbModelClientImpl) Initiator(com.emc.storageos.db.client.model.Initiator) ArrayList(java.util.ArrayList) URIQueryResultList(com.emc.storageos.db.client.constraint.URIQueryResultList) List(java.util.List) ExportPathParams(com.emc.storageos.db.client.model.ExportPathParams) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Example 8 with ExportPathParams

use of com.emc.storageos.db.client.model.ExportPathParams in project coprhd-controller by CoprHD.

the class VmaxMaskingOrchestrator method changePortGroup.

@Override
public void changePortGroup(URI storageURI, URI exportGroupURI, URI portGroupURI, List<URI> exportMaskURIs, boolean waitForApproval, String token) {
    ExportChangePortGroupCompleter taskCompleter = null;
    try {
        ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
        StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
        StoragePortGroup portGroup = _dbClient.queryObject(StoragePortGroup.class, portGroupURI);
        taskCompleter = new ExportChangePortGroupCompleter(storageURI, exportGroupURI, token, portGroupURI);
        logExportGroup(exportGroup, storageURI);
        String workflowKey = "changePortGroup";
        if (_workflowService.hasWorkflowBeenCreated(token, workflowKey)) {
            return;
        }
        Workflow workflow = _workflowService.getNewWorkflow(MaskingWorkflowEntryPoints.getInstance(), workflowKey, false, token);
        if (CollectionUtils.isEmpty(exportMaskURIs)) {
            _log.info("No export masks to change");
            taskCompleter.ready(_dbClient);
            return;
        }
        List<ExportMask> exportMasks = _dbClient.queryObject(ExportMask.class, exportMaskURIs);
        String previousStep = null;
        Set<URI> hostURIs = new HashSet<URI>();
        for (ExportMask oldMask : exportMasks) {
            // create a new masking view using the new port group
            SmisStorageDevice device = (SmisStorageDevice) getDevice();
            oldMask = device.refreshExportMask(storage, oldMask);
            StringSet existingInits = oldMask.getExistingInitiators();
            StringMap existingVols = oldMask.getExistingVolumes();
            if (!CollectionUtils.isEmpty(existingInits)) {
                String error = String.format("The export mask %s has unmanaged initiators %s", oldMask.getMaskName(), Joiner.on(',').join(existingInits));
                _log.error(error);
                ServiceError serviceError = DeviceControllerException.errors.changePortGroupValidationError(error);
                taskCompleter.error(_dbClient, serviceError);
                return;
            }
            if (!CollectionUtils.isEmpty(existingVols)) {
                String error = String.format("The export mask %s has unmanaged volumes %s", oldMask.getMaskName(), Joiner.on(',').join(existingVols.keySet()));
                _log.error(error);
                ServiceError serviceError = DeviceControllerException.errors.changePortGroupValidationError(error);
                taskCompleter.error(_dbClient, serviceError);
                return;
            }
            InitiatorHelper initiatorHelper = new InitiatorHelper(StringSetUtil.stringSetToUriList(oldMask.getInitiators())).process(exportGroup);
            List<String> initiatorNames = initiatorHelper.getPortNames();
            List<URI> volumes = StringSetUtil.stringSetToUriList(oldMask.getVolumes().keySet());
            ExportPathParams pathParams = _blockScheduler.calculateExportPathParamForVolumes(volumes, 0, storageURI, exportGroupURI);
            pathParams.setStoragePorts(portGroup.getStoragePorts());
            List<Initiator> initiators = ExportUtils.getExportMaskInitiators(oldMask, _dbClient);
            List<URI> initURIs = new ArrayList<URI>();
            for (Initiator init : initiators) {
                if (!NullColumnValueGetter.isNullURI(init.getHost())) {
                    hostURIs.add(init.getHost());
                }
                initURIs.add(init.getId());
            }
            // Get impacted export groups
            List<ExportGroup> impactedExportGroups = ExportMaskUtils.getExportGroups(_dbClient, oldMask);
            List<URI> exportGroupURIs = URIUtil.toUris(impactedExportGroups);
            _log.info("changePortGroup: exportMask {}, impacted export groups: {}", oldMask.getMaskName(), Joiner.on(',').join(exportGroupURIs));
            Map<URI, List<URI>> assignments = _blockScheduler.assignStoragePorts(storage, exportGroup, initiators, null, pathParams, volumes, _networkDeviceController, exportGroup.getVirtualArray(), token);
            // Trying to find if there is existing export mask or masking view for the same host and using the new
            // port group. If found one, add the volumes in the current export mask to the new one; otherwise, create
            // a new export mask/masking view, with the same storage group, initiator group and the new port group.
            // then delete the current export mask.
            ExportMask newMask = device.findExportMasksForPortGroupChange(storage, initiatorNames, portGroupURI);
            Map<URI, Integer> volumesToAdd = StringMapUtil.stringMapToVolumeMap(oldMask.getVolumes());
            if (newMask != null) {
                updateZoningMap(exportGroup, newMask, true);
                _log.info(String.format("adding these volumes %s to mask %s", Joiner.on(",").join(volumesToAdd.keySet()), newMask.getMaskName()));
                previousStep = generateZoningAddVolumesWorkflow(workflow, previousStep, exportGroup, Arrays.asList(newMask), new ArrayList<URI>(volumesToAdd.keySet()));
                String addVolumeStep = workflow.createStepId();
                ExportTaskCompleter exportTaskCompleter = new ExportMaskAddVolumeCompleter(exportGroupURI, newMask.getId(), volumesToAdd, addVolumeStep);
                exportTaskCompleter.setExportGroups(exportGroupURIs);
                Workflow.Method maskingExecuteMethod = new Workflow.Method("doExportGroupAddVolumes", storageURI, exportGroupURI, newMask.getId(), volumesToAdd, null, exportTaskCompleter);
                Workflow.Method maskingRollbackMethod = new Workflow.Method("rollbackExportGroupAddVolumes", storageURI, exportGroupURI, exportGroupURIs, newMask.getId(), volumesToAdd, initURIs, addVolumeStep);
                previousStep = workflow.createStep(EXPORT_GROUP_MASKING_TASK, String.format("Adding volumes to mask %s (%s)", newMask.getMaskName(), newMask.getId().toString()), previousStep, storageURI, storage.getSystemType(), MaskingWorkflowEntryPoints.class, maskingExecuteMethod, maskingRollbackMethod, addVolumeStep);
                previousStep = generateExportMaskAddVolumesWorkflow(workflow, previousStep, storage, exportGroup, newMask, volumesToAdd, null);
            } else {
                // We don't find existing export mask /masking view, we will create a new one.
                // first, to construct the new export mask name, if the export mask has the original name, then
                // append the new port group name to the current export mask name; if the export mask already has the current
                // port group name appended, then remove the current port group name, and append the new one.
                String oldName = oldMask.getMaskName();
                URI oldPGURI = oldMask.getPortGroup();
                if (oldPGURI != null) {
                    StoragePortGroup oldPG = _dbClient.queryObject(StoragePortGroup.class, oldPGURI);
                    if (oldPG != null) {
                        String pgName = oldPG.getLabel();
                        if (oldName.endsWith(pgName)) {
                            oldName = oldName.replaceAll(pgName, "");
                        }
                    }
                }
                String maskName = null;
                if (oldName.endsWith("_")) {
                    maskName = String.format("%s%s", oldName, portGroup.getLabel());
                } else {
                    maskName = String.format("%s_%s", oldName, portGroup.getLabel());
                }
                newMask = ExportMaskUtils.initializeExportMask(storage, exportGroup, initiators, volumesToAdd, getStoragePortsInPaths(assignments), assignments, maskName, _dbClient);
                newMask.setPortGroup(portGroupURI);
                List<BlockObject> vols = new ArrayList<BlockObject>();
                for (URI boURI : volumesToAdd.keySet()) {
                    BlockObject bo = BlockObject.fetch(_dbClient, boURI);
                    vols.add(bo);
                }
                newMask.addToUserCreatedVolumes(vols);
                _dbClient.updateObject(newMask);
                _log.info(String.format("Creating new exportMask %s", maskName));
                // Make a new TaskCompleter for the exportStep. It has only one subtask.
                // This is due to existing requirements in the doExportGroupCreate completion
                // logic.
                String maskingStep = workflow.createStepId();
                ExportTaskCompleter exportTaskCompleter = new ExportMaskChangePortGroupAddMaskCompleter(newMask.getId(), exportGroupURI, maskingStep);
                exportTaskCompleter.setExportGroups(exportGroupURIs);
                Workflow.Method maskingExecuteMethod = new Workflow.Method("doExportChangePortGroupAddPaths", storageURI, exportGroupURI, newMask.getId(), oldMask.getId(), portGroupURI, exportTaskCompleter);
                Workflow.Method maskingRollbackMethod = new Workflow.Method("rollbackExportGroupCreate", storageURI, exportGroupURI, newMask.getId(), maskingStep);
                maskingStep = workflow.createStep(EXPORT_GROUP_MASKING_TASK, String.format("Create export mask(%s) to use port group %s", newMask.getMaskName(), portGroup.getNativeGuid()), previousStep, storageURI, storage.getSystemType(), MaskingWorkflowEntryPoints.class, maskingExecuteMethod, maskingRollbackMethod, maskingStep);
                String zoningStep = workflow.createStepId();
                List<URI> masks = new ArrayList<URI>();
                masks.add(newMask.getId());
                previousStep = generateZoningCreateWorkflow(workflow, maskingStep, exportGroup, masks, volumesToAdd, zoningStep);
            }
        }
        previousStep = _wfUtils.generateHostRescanWorkflowSteps(workflow, hostURIs, previousStep);
        if (waitForApproval) {
            // Insert a step that will be suspended. When it resumes, it will re-acquire the lock keys,
            // which are released when the workflow suspends.
            List<String> lockKeys = ControllerLockingUtil.getHostStorageLockKeys(_dbClient, ExportGroup.ExportGroupType.valueOf(exportGroup.getType()), StringSetUtil.stringSetToUriList(exportGroup.getInitiators()), storageURI);
            String suspendMessage = "Adjust/rescan host/cluster paths. Press \"Resume\" to start removal of unnecessary paths." + "\"Rollback\" will terminate the order and roll back";
            Workflow.Method method = WorkflowService.acquireWorkflowLocksMethod(lockKeys, LockTimeoutValue.get(LockType.EXPORT_GROUP_OPS));
            Workflow.Method rollbackNull = Workflow.NULL_METHOD;
            previousStep = workflow.createStep("AcquireLocks", "Suspending for user verification of host/cluster connectivity.", previousStep, storage.getId(), storage.getSystemType(), WorkflowService.class, method, rollbackNull, waitForApproval, null);
            workflow.setSuspendedStepMessage(previousStep, suspendMessage);
        }
        for (ExportMask exportMask : exportMasks) {
            previousStep = generateChangePortGroupDeleteMaskWorkflowstep(storageURI, exportGroup, exportMask, previousStep, workflow);
        }
        _wfUtils.generateHostRescanWorkflowSteps(workflow, hostURIs, previousStep);
        if (!workflow.getAllStepStatus().isEmpty()) {
            _log.info("The change port group workflow has {} steps. Starting the workflow.", workflow.getAllStepStatus().size());
            // update ExportChangePortGroupCompleter with affected export groups
            Set<URI> affectedExportGroups = new HashSet<URI>();
            for (ExportMask mask : exportMasks) {
                List<ExportGroup> assocExportGroups = ExportMaskUtils.getExportGroups(_dbClient, mask);
                for (ExportGroup eg : assocExportGroups) {
                    affectedExportGroups.add(eg.getId());
                }
            }
            taskCompleter.setAffectedExportGroups(affectedExportGroups);
            workflow.executePlan(taskCompleter, "Change port group successfully.");
            _workflowService.markWorkflowBeenCreated(token, workflowKey);
        } else {
            taskCompleter.ready(_dbClient);
        }
    } catch (Exception e) {
        _log.error("Export change port group Orchestration failed.", e);
        if (taskCompleter != null) {
            ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(e.getMessage(), e);
            taskCompleter.error(_dbClient, serviceError);
        }
    }
}
Also used : StoragePortGroup(com.emc.storageos.db.client.model.StoragePortGroup) StringMap(com.emc.storageos.db.client.model.StringMap) ExportTaskCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportTaskCompleter) Lists.newArrayList(com.google.common.collect.Lists.newArrayList) ArrayList(java.util.ArrayList) URI(java.net.URI) Initiator(com.emc.storageos.db.client.model.Initiator) WorkflowService(com.emc.storageos.workflow.WorkflowService) StringSet(com.emc.storageos.db.client.model.StringSet) 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) ExportMaskChangePortGroupAddMaskCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskChangePortGroupAddMaskCompleter) Workflow(com.emc.storageos.workflow.Workflow) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) ExportGroup(com.emc.storageos.db.client.model.ExportGroup) ExportChangePortGroupCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportChangePortGroupCompleter) ExportMaskAddVolumeCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskAddVolumeCompleter) SmisStorageDevice(com.emc.storageos.volumecontroller.impl.smis.SmisStorageDevice) ExportPathParams(com.emc.storageos.db.client.model.ExportPathParams)

Example 9 with ExportPathParams

use of com.emc.storageos.db.client.model.ExportPathParams in project coprhd-controller by CoprHD.

the class BlockStorageScheduler method updateZoningMap.

/**
 * Updates the ExportMask's zoning map after the initiator to port associations
 * have been discovered from an array like Cinder. This routine is needed when we
 * are masking first, and cannot tell the array what ports are assigned to what initiators,
 * i.e. rather the array tells us what it did. In particular, this is applicable to storage
 * arrays managed by Southbound SDK drivers.
 * This routine is not needed when the array can be told what initiators to map to what ports.
 * 1. All zoning map entries for the initiators in the mask are removed.
 * 2. For the targets in the mask, they are paired with the initiators they can service,
 * i.e. that are on the same or a routeable network, and are usable in the varray,
 * and the corresponding zones are put in the zoning map.
 * Then the path parameters are enforced, based on the path parameter data discovered from
 * the ExportMask.
 * 3. Any initiators with more than paths_per_initiator ports are reduced to have
 * only pathsPerInitiator number of ports. We try not to remove the same port from
 * multiple initiators. (Note: we do not declare it an error if there are fewer
 * than pathsPerInitiator ports assigned to an initiator.)
 * 4. Then we verify we are not over maxPaths. This is done by counting up the number of
 * paths currently in the mask, and if there are excess, cycling through the networks
 * (starting with the network with the most initiators) and removing initiators until
 * we are under paths per initiator.
 * 5. Finally we sum up the paths in the mask, and verify that we have at least as
 * many as minPaths.
 *
 * @param mask -- The ExportMask being manipulated
 * @param varray -- The Virtual Array (normally from the ExportGroup)
 * @param exportGroupURI -- URI of the ExportGroup
 *
 *            Assumption: the export mask has up to date initiators and storage ports
 */
public void updateZoningMap(ExportMask mask, URI varray, URI exportGroupURI) {
    // Convert the volumes to a Collection.
    List<URI> volumeURIs = ExportMaskUtils.getVolumeURIs(mask);
    // Determine the number of paths required for the volumes in the export mask.
    ExportPathParams pathParams = calculateExportPathParamForVolumes(volumeURIs, 0, mask.getStorageDevice(), exportGroupURI);
    _log.info(String.format("Updating zoning map for ExportMask %s (%s) pathParams %s", mask.getMaskName(), mask.getId(), pathParams.toString()));
    // Data structures for mapping Network to Initiators and Network to StoragePorts
    Map<URI, Set<Initiator>> network2InitiatorsMap = new HashMap<URI, Set<Initiator>>();
    Map<URI, Set<StoragePort>> network2PortsMap = new HashMap<URI, Set<StoragePort>>();
    _log.debug("Export Mask before zoning map update -" + mask.toString());
    // so it is not considered by getInitiatorPortsInMask().
    for (String initiatorURIStr : mask.getZoningMap().keySet()) {
        mask.removeZoningMapEntry(initiatorURIStr);
    }
    mask.setZoningMap(null);
    // corresponding to the Initiator.
    for (String initiatorURIStr : mask.getInitiators()) {
        Initiator initiator = _dbClient.queryObject(Initiator.class, URI.create(initiatorURIStr));
        if (initiator == null || initiator.getInactive()) {
            continue;
        }
        // Add the initiator to the net2InitiatorsMap
        NetworkLite initiatorNetwork = getInitiatorNetwork(initiator, _dbClient);
        if (!network2InitiatorsMap.containsKey(initiatorNetwork.getId())) {
            network2InitiatorsMap.put(initiatorNetwork.getId(), new HashSet<Initiator>());
        }
        network2InitiatorsMap.get(initiatorNetwork.getId()).add(initiator);
        List<URI> storagePortList = ExportUtils.getPortsInInitiatorNetwork(mask, initiator, _dbClient);
        if (storagePortList.isEmpty()) {
            continue;
        }
        StringSet storagePorts = new StringSet();
        for (URI portURI : storagePortList) {
            StoragePort port = _dbClient.queryObject(StoragePort.class, portURI);
            URI portNetworkId = port.getNetwork();
            if (!DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name().equals(port.getCompatibilityStatus()) || !DiscoveryStatus.VISIBLE.name().equals(port.getDiscoveryStatus()) || NullColumnValueGetter.isNullURI(portNetworkId) || !port.getRegistrationStatus().equals(StoragePort.RegistrationStatus.REGISTERED.name()) || StoragePort.OperationalStatus.NOT_OK.equals(StoragePort.OperationalStatus.valueOf(port.getOperationalStatus())) || StoragePort.PortType.valueOf(port.getPortType()) != StoragePort.PortType.frontend) {
                _log.debug("Storage port {} is not selected because it is inactive, is not compatible, is not visible, not on a network, " + "is not registered, has a status other than OK, or is not a frontend port", port.getLabel());
                continue;
            }
            // Network cnnectivity was checked in getInitiatorPortsInMask()
            if (port.getTaggedVirtualArrays().contains(varray.toString())) {
                storagePorts.add(portURI.toString());
                if (!network2PortsMap.containsKey(portNetworkId)) {
                    network2PortsMap.put(portNetworkId, new HashSet<StoragePort>());
                }
                network2PortsMap.get(portNetworkId).add(port);
            } else {
                _log.debug("Storage port {} is not selected because it is not in the specified varray {}", port.getLabel(), varray.toString());
            }
        }
        mask.addZoningMapEntry(initiatorURIStr, storagePorts);
    }
    _log.debug("Export Mask after zoning map update -" + mask.toString());
    // Now that we have constructed an initial cut at the zoning map, enforce the path parameters.
    // 1. Ensure that no initiator has more than the paths_per_initiator variable allows.
    // For every initiator, make sure it doesn't have more than paths_per_initiator ports.
    // Try not to remove the same port multiple times.
    Set<String> removedPorts = new HashSet<String>();
    for (URI networkURI : network2InitiatorsMap.keySet()) {
        for (Initiator initiator : network2InitiatorsMap.get(networkURI)) {
            StringSet ports = mask.getZoningMap().get(initiator.getId().toString());
            if ((null == ports) || (ports.size() <= pathParams.getPathsPerInitiator())) {
                continue;
            }
            _log.info(String.format("Limiting paths for initiator %s to %s; initial ports %s", initiator.getInitiatorPort(), pathParams.getPathsPerInitiator().toString(), ports));
            boolean removedPort = true;
            outer: while (removedPort && ports.size() > pathParams.getPathsPerInitiator()) {
                // First try not removing an already removed port
                removedPort = false;
                for (String port : ports) {
                    if (!removedPorts.contains(port)) {
                        removedPorts.add(port);
                        ports.remove(port);
                        removedPort = true;
                        continue outer;
                    }
                }
                // As a last resort, remove a port that is duplicated
                for (String port : ports) {
                    removedPorts.add(port);
                    ports.remove(port);
                    removedPort = true;
                    continue outer;
                }
            }
            _log.info(String.format("Limited ports for initiator %s to %s", initiator.getInitiatorPort(), ports));
        }
    }
    // Now check that the total number of entries is not higher than maxPaths.
    // Remove paths from Networks with the most initiators to the list by removing initiators.
    ExportPathParams currentPathParams = calculateExportPathParamForExportMask(_dbClient, mask);
    Integer overMaxPaths = currentPathParams.getMaxPaths() - pathParams.getMaxPaths();
    // Make a sorted map of initiator count to networks.
    SortedMap<Integer, Set<URI>> initiatorCountToNetwork = new TreeMap<Integer, Set<URI>>();
    for (URI networkURI : network2InitiatorsMap.keySet()) {
        Integer count = network2InitiatorsMap.get(networkURI).size();
        if (!initiatorCountToNetwork.containsKey(count)) {
            initiatorCountToNetwork.put(count, new HashSet<URI>());
        }
        initiatorCountToNetwork.get(count).add(networkURI);
    }
    while (overMaxPaths > 0) {
        // Go backwards from last key (highest count) to first (lowest count).
        Integer lastKey = initiatorCountToNetwork.lastKey();
        Integer firstKey = initiatorCountToNetwork.firstKey();
        for (Integer count = lastKey; overMaxPaths > 0 && count >= firstKey; count--) {
            // Remove an Initiator from each network
            Set<URI> networks = initiatorCountToNetwork.get(count);
            if (networks == null) {
                continue;
            }
            for (URI networkURI : networks) {
                Iterator<Initiator> iter = network2InitiatorsMap.get(networkURI).iterator();
                if (iter.hasNext()) {
                    // Remove an initiator
                    Initiator initiator = iter.next();
                    StringSet ports = mask.getZoningMap().get(initiator.getId().toString());
                    overMaxPaths -= ports.size();
                    _log.info(String.format("Removing initiator %s to comply with maxPaths", initiator.getInitiatorPort()));
                    mask.removeZoningMapEntry(initiator.getId().toString());
                    network2InitiatorsMap.get(networkURI).remove(initiator);
                }
                if (overMaxPaths <= 0) {
                    break;
                }
            }
        }
    }
    // Finally, count the resulting number of paths, and make sure it is over minPaths.
    Integer pathCount = 0;
    Integer initiatorCount = 0;
    for (String initiatorId : mask.getZoningMap().keySet()) {
        initiatorCount++;
        StringSet ports = mask.getZoningMap().get(initiatorId);
        pathCount += ports.size();
    }
    _log.info(String.format("ExportMask %s (%s) pathCount %s", mask.getMaskName(), mask.getId(), pathCount.toString()));
    if (pathCount < pathParams.getMinPaths()) {
        throw PlacementException.exceptions.cannotAllocateMinPaths(pathParams.getMinPaths(), initiatorCount, pathParams.getPathsPerInitiator(), pathParams.getMinPaths(), pathParams.getMaxPaths());
    }
    // Save the updated ExportMask
    _dbClient.updateObject(mask);
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) StringSet(com.emc.storageos.db.client.model.StringSet) HashMap(java.util.HashMap) NetworkLite(com.emc.storageos.util.NetworkLite) StoragePort(com.emc.storageos.db.client.model.StoragePort) TreeMap(java.util.TreeMap) URI(java.net.URI) Initiator(com.emc.storageos.db.client.model.Initiator) StringSet(com.emc.storageos.db.client.model.StringSet) ExportPathParams(com.emc.storageos.db.client.model.ExportPathParams) HashSet(java.util.HashSet)

Example 10 with ExportPathParams

use of com.emc.storageos.db.client.model.ExportPathParams in project coprhd-controller by CoprHD.

the class BlockStorageScheduler method getExportPathParam.

/**
 * Get the ExportPathParams (maxPaths and pathsPerInitiator variables)
 * from the VirtualPool belonging to a volume
 * (or the parent volume of a snapshot).
 * For backward compatibility, if the fields other than num_paths in the
 * Vpool are empty, they are defaulted.
 *
 * @TODO For ingestion, if there are more than 1 supported virtual pool,
 *       then consider the path params with least path settings.
 *
 * @param block
 * @return Integer num_paths from VirtualPool
 */
public static ExportPathParams getExportPathParam(BlockObject block, URI vPoolURI, DbClient dbClient) {
    if (vPoolURI == null) {
        return ExportPathParams.getDefaultParams();
    }
    VirtualPool vPool = dbClient.queryObject(VirtualPool.class, vPoolURI);
    if (vPool == null) {
        return ExportPathParams.getDefaultParams();
    }
    if (vPool.getNumPaths() == null) {
        return ExportPathParams.getDefaultParams();
    }
    Integer minPaths = vPool.getMinPaths();
    if (minPaths == null) {
        minPaths = 0;
    }
    Integer pathsPerInitiator = vPool.getPathsPerInitiator();
    if (pathsPerInitiator == null) {
        pathsPerInitiator = 0;
    }
    return new ExportPathParams(vPool.getNumPaths(), minPaths, pathsPerInitiator);
}
Also used : VirtualPool(com.emc.storageos.db.client.model.VirtualPool) ExportPathParams(com.emc.storageos.db.client.model.ExportPathParams)

Aggregations

ExportPathParams (com.emc.storageos.db.client.model.ExportPathParams)39 URI (java.net.URI)33 ArrayList (java.util.ArrayList)26 List (java.util.List)21 URIQueryResultList (com.emc.storageos.db.client.constraint.URIQueryResultList)17 Initiator (com.emc.storageos.db.client.model.Initiator)17 HashMap (java.util.HashMap)17 ExportMask (com.emc.storageos.db.client.model.ExportMask)13 HashSet (java.util.HashSet)13 ExportGroup (com.emc.storageos.db.client.model.ExportGroup)12 NamedURI (com.emc.storageos.db.client.model.NamedURI)12 Map (java.util.Map)11 StringMap (com.emc.storageos.db.client.model.StringMap)8 StoragePortGroup (com.emc.storageos.db.client.model.StoragePortGroup)6 StringSet (com.emc.storageos.db.client.model.StringSet)6 DeviceControllerException (com.emc.storageos.exceptions.DeviceControllerException)6 ApplicationAddVolumeList (com.emc.storageos.volumecontroller.ApplicationAddVolumeList)6 Workflow (com.emc.storageos.workflow.Workflow)6 BlockObject (com.emc.storageos.db.client.model.BlockObject)5 StoragePort (com.emc.storageos.db.client.model.StoragePort)5