Search in sources :

Example 21 with VPlexApiException

use of com.emc.storageos.vplex.api.VPlexApiException in project coprhd-controller by CoprHD.

the class VPlexDeviceController method createStorageView.

/**
 * A Workflow Step to create a Storage View on the VPlex.
 *
 * @param vplexURI
 * @param exportURI
 * @param blockObjectMap
 *            - A list of Volume/Snapshot URIs to LUN ids.
 * @param initiators
 * @param stepId
 * @throws ControllerException
 */
public void createStorageView(URI vplexURI, URI exportURI, URI exportMaskURI, Map<URI, Integer> blockObjectMap, List<URI> initiators, List<URI> targets, ExportMaskCreateCompleter completer, String stepId) throws ControllerException {
    String lockName = null;
    boolean lockAcquired = false;
    try {
        WorkflowStepCompleter.stepExecuting(stepId);
        StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
        ExportMask exportMask = getDataObject(ExportMask.class, exportMaskURI, _dbClient);
        VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
        // Setup the call to the VPlex API.
        List<PortInfo> targetPortInfos = new ArrayList<PortInfo>();
        for (URI target : targets) {
            StoragePort port = getDataObject(StoragePort.class, target, _dbClient);
            PortInfo pi = new PortInfo(port.getPortNetworkId().toUpperCase().replaceAll(":", ""), null, port.getPortName(), null);
            targetPortInfos.add(pi);
            if (lockName == null) {
                String clusterId = ConnectivityUtil.getVplexClusterOfPort(port);
                lockName = _vplexApiLockManager.getLockName(vplexURI, clusterId);
            }
        }
        List<PortInfo> initiatorPortInfos = new ArrayList<PortInfo>();
        for (URI init : initiators) {
            Initiator initiator = getDataObject(Initiator.class, init, _dbClient);
            PortInfo pi = new PortInfo(initiator.getInitiatorPort().toUpperCase().replaceAll(":", ""), initiator.getInitiatorNode().toUpperCase().replaceAll(":", ""), initiator.getLabel(), getVPlexInitiatorType(initiator));
            initiatorPortInfos.add(pi);
        }
        List<BlockObject> blockObjects = new ArrayList<BlockObject>();
        Map<String, Integer> boMap = new HashMap<String, Integer>();
        for (URI vol : blockObjectMap.keySet()) {
            Integer lun = blockObjectMap.get(vol);
            if (lun == null) {
                lun = ExportGroup.LUN_UNASSIGNED;
            }
            BlockObject bo = Volume.fetchExportMaskBlockObject(_dbClient, vol);
            blockObjects.add(bo);
            boMap.put(bo.getDeviceLabel(), lun);
        }
        lockAcquired = _vplexApiLockManager.acquireLock(lockName, LockTimeoutValue.get(LockType.VPLEX_API_LIB));
        if (!lockAcquired) {
            throw VPlexApiException.exceptions.couldNotObtainConcurrencyLock(vplex.getLabel());
        }
        VPlexStorageViewInfo svInfo = client.createStorageView(exportMask.getMaskName(), targetPortInfos, initiatorPortInfos, boMap);
        // When VPLEX volumes or snapshots are exported to a storage view, they get a WWN,
        // so set the WWN from the returned storage view information.
        Map<URI, Integer> updatedBlockObjectMap = new HashMap<URI, Integer>();
        for (BlockObject bo : blockObjects) {
            String deviceLabel = bo.getDeviceLabel();
            bo.setWWN(svInfo.getWWNForStorageViewVolume(deviceLabel));
            _dbClient.updateObject(bo);
            updatedBlockObjectMap.put(bo.getId(), svInfo.getHLUForStorageViewVolume(deviceLabel));
        }
        // We also need to update the volume/lun id map in the export mask
        // to those assigned by the VPLEX.
        _log.info("Updating volume/lun map in export mask {}", exportMask.getId());
        _log.info("updatedBlockObjectMap: " + updatedBlockObjectMap.toString());
        exportMask.addVolumes(updatedBlockObjectMap);
        // Update user created volumes
        exportMask.addToUserCreatedVolumes(blockObjects);
        // update native id (this is the context path to the storage view on the vplex)
        // e.g.: /clusters/cluster-1/exports/storage-views/V1_myserver_288
        // this column being set to non-null can be used to indicate a storage view that
        // has been successfully created on the VPLEX device
        exportMask.setNativeId(svInfo.getPath());
        _dbClient.updateObject(exportMask);
        completer.ready(_dbClient);
    } catch (VPlexApiException vae) {
        _log.error("Exception creating VPlex Storage View: " + vae.getMessage(), vae);
        if ((null != vae.getMessage()) && vae.getMessage().toUpperCase().contains(VPlexApiConstants.DUPLICATE_STORAGE_VIEW_ERROR_FRAGMENT.toUpperCase())) {
            _log.error("storage view creation failure is due to duplicate storage view name");
            ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
            if (null != exportMask) {
                _log.error("marking ExportMask inactive so that rollback will " + "not delete the existing storage view on the VPLEX");
                _dbClient.removeObject(exportMask);
            }
        }
        failStep(completer, stepId, vae);
    } catch (Exception ex) {
        _log.error("Exception creating VPlex Storage View: " + ex.getMessage(), ex);
        String opName = ResourceOperationTypeEnum.CREATE_STORAGE_VIEW.getName();
        ServiceError serviceError = VPlexApiException.errors.createStorageViewFailed(opName, ex);
        failStep(completer, stepId, serviceError);
    } finally {
        if (lockAcquired) {
            _vplexApiLockManager.releaseLock(lockName);
        }
    }
}
Also used : ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) VPlexStorageViewInfo(com.emc.storageos.vplex.api.VPlexStorageViewInfo) HashMap(java.util.HashMap) ExportMask(com.emc.storageos.db.client.model.ExportMask) ArrayList(java.util.ArrayList) StoragePort(com.emc.storageos.db.client.model.StoragePort) 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) Initiator(com.emc.storageos.db.client.model.Initiator) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient) BlockObject(com.emc.storageos.db.client.model.BlockObject) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Example 22 with VPlexApiException

use of com.emc.storageos.vplex.api.VPlexApiException in project coprhd-controller by CoprHD.

the class VPlexDeviceController method createVirtualVolumes.

/**
 * Do the creation of a VPlex Virtual Volume. This is called as a Workflow Step.
 * NOTE: The parameters here must match createVirtualVolumesMethod above (except stepId).
 *
 * @param vplexURI
 *            -- URI of the VPlex StorageSystem
 * @param vplexVolumeURIs
 *            -- URI of the VPlex volumes to be created. They must contain
 *            associatedVolumes (URI of the underlying Storage Volumes).
 * @param computeResourceMap
 *            A Map of the compute resource for each volume.
 * @param stepId
 *            - The stepId used for completion.
 * @throws WorkflowException
 */
public void createVirtualVolumes(URI vplexURI, List<URI> vplexVolumeURIs, Map<URI, URI> computeResourceMap, String stepId) throws WorkflowException {
    List<List<VolumeInfo>> rollbackData = new ArrayList<List<VolumeInfo>>();
    List<URI> createdVplexVolumeURIs = new ArrayList<URI>();
    try {
        WorkflowStepCompleter.stepExecuting(stepId);
        // Get the API client.
        StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
        VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
        // Make a map of StorageSystem ids to Storage System
        Map<URI, StorageSystem> storageMap = new HashMap<URI, StorageSystem>();
        // Make a map of Virtual Volumes to Storage Volumes.
        Map<Volume, List<Volume>> volumeMap = new HashMap<Volume, List<Volume>>();
        // Make a string buffer for volume labels
        StringBuffer volumeLabels = new StringBuffer();
        // List of storage system Guids
        List<String> storageSystemGuids = new ArrayList<String>();
        Boolean isDistributedVolume = false;
        Map<String, Set<URI>> clusterVarrayMap = new HashMap<>();
        for (URI vplexVolumeURI : vplexVolumeURIs) {
            Volume vplexVolume = getDataObject(Volume.class, vplexVolumeURI, _dbClient);
            URI vplexVolumeVarrayURI = vplexVolume.getVirtualArray();
            String clusterId = ConnectivityUtil.getVplexClusterForVarray(vplexVolumeVarrayURI, vplexVolume.getStorageController(), _dbClient);
            if (clusterVarrayMap.containsKey(clusterId)) {
                clusterVarrayMap.get(clusterId).add(vplexVolumeVarrayURI);
            } else {
                Set<URI> varraysForCluster = new HashSet<>();
                varraysForCluster.add(vplexVolumeVarrayURI);
                clusterVarrayMap.put(clusterId, varraysForCluster);
            }
            volumeLabels.append(vplexVolume.getLabel()).append(" ");
            volumeMap.put(vplexVolume, new ArrayList<Volume>());
            // Find the underlying Storage Volumes
            StringSet associatedVolumes = vplexVolume.getAssociatedVolumes();
            if (associatedVolumes.size() > 1) {
                isDistributedVolume = true;
            }
            for (String associatedVolume : associatedVolumes) {
                Volume storageVolume = getDataObject(Volume.class, new URI(associatedVolume), _dbClient);
                URI storageSystemId = storageVolume.getStorageController();
                if (storageMap.containsKey(storageSystemId) == false) {
                    StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageSystemId);
                    storageMap.put(storageSystemId, storage);
                    if (!storageSystemGuids.contains(storage.getNativeGuid())) {
                        storageSystemGuids.add(storage.getNativeGuid());
                    }
                }
                volumeMap.get(vplexVolume).add(storageVolume);
            }
        }
        _log.info(String.format("Request to create: %s virtual volume(s) %s", volumeMap.size(), volumeLabels));
        long startTime = System.currentTimeMillis();
        // If a new backend system is connected to a VPLEX and the VPLEX does not
        // yet know about the system i.e., the system does not show up in the path
        // /clusters/cluster-x/storage-elements/storage-arrays, and a user attempts
        // to create a virtual volume, the request may fail because we cannot find
        // the storage system. When the backend volume on the new system is created
        // and exported to the VPLEX, the VPLEX will recognize new system. However,
        // this may not occur immediately. So, when we go to create the vplex volume
        // using that backend volume, we may not find that system and volume on the
        // first try. We saw this in development. As such there was a retry loop
        // added when finding the backend volumes in the discovery that is performed
        // in the method to create the virtual volume.
        // 
        // However changes for CTRL-12826 were merged on 7/31/2015 that circumvented
        // that retry code. Changes were made to do the array re-discover here prior
        // to virtual volume creation, rather than during virtual volume creation and
        // false was passed to the create virtual volume routine for the discovery
        // required flag. The newly added call does not do any kind of retry if the
        // system is not found and so a failure will occur in the scenario described
        // above. If a system is not found an exception is thrown. Now we will catch
        // that exception and re-enable discovery in the volume creation routine.
        // Essentially we revert to what was happening before the 12826 changes if there
        // is an issue discovering the systems on the initial try here.
        boolean discoveryRequired = false;
        try {
            client.rediscoverStorageSystems(storageSystemGuids);
        } catch (Exception e) {
            String warnMsg = String.format("Initial discovery of one or more of these backend systems %s failed: %s." + "Discovery is required during virtual volume creation", storageSystemGuids, e.getMessage());
            _log.warn(warnMsg);
            discoveryRequired = true;
        }
        // Now make a call to the VPlexAPIClient.createVirtualVolume for each vplex volume.
        StringBuilder buf = new StringBuilder();
        buf.append("Vplex: " + vplexURI + " created virtual volume(s): ");
        boolean thinEnabled = false;
        boolean searchAllClustersForStorageVolumes = ((clusterVarrayMap.keySet().size() > 1 || isDistributedVolume) ? true : false);
        List<VPlexVirtualVolumeInfo> virtualVolumeInfos = new ArrayList<VPlexVirtualVolumeInfo>();
        Map<String, Volume> vplexVolumeNameMap = new HashMap<String, Volume>();
        List<VPlexClusterInfo> clusterInfoList = null;
        for (Volume vplexVolume : volumeMap.keySet()) {
            URI vplexVolumeId = vplexVolume.getId();
            _log.info(String.format("Creating virtual volume: %s (%s)", vplexVolume.getLabel(), vplexVolumeId));
            URI vplexVolumeVarrayURI = vplexVolume.getVirtualArray();
            String clusterId = null;
            for (Entry<String, Set<URI>> clusterEntry : clusterVarrayMap.entrySet()) {
                if (clusterEntry.getValue().contains(vplexVolumeVarrayURI)) {
                    clusterId = clusterEntry.getKey();
                }
            }
            List<VolumeInfo> vinfos = new ArrayList<VolumeInfo>();
            for (Volume storageVolume : volumeMap.get(vplexVolume)) {
                StorageSystem storage = storageMap.get(storageVolume.getStorageController());
                List<String> itls = VPlexControllerUtils.getVolumeITLs(storageVolume);
                VolumeInfo info = new VolumeInfo(storage.getNativeGuid(), storage.getSystemType(), storageVolume.getWWN().toUpperCase().replaceAll(":", ""), storageVolume.getNativeId(), storageVolume.getThinlyProvisioned().booleanValue(), itls);
                if (storageVolume.getVirtualArray().equals(vplexVolumeVarrayURI)) {
                    // We always want the source backend volume identified first. It
                    // may not be first in the map as the map is derived from the
                    // VPLEX volume's associated volumes list which is an unordered
                    // StringSet.
                    vinfos.add(0, info);
                } else {
                    vinfos.add(info);
                }
                if (info.getIsThinProvisioned()) {
                    // if either or both legs of distributed is thin, try for thin-enabled
                    // (or if local and the single backend volume is thin, try as well)
                    thinEnabled = true;
                }
            }
            // Update rollback information.
            rollbackData.add(vinfos);
            _workflowService.storeStepData(stepId, rollbackData);
            InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_045);
            // Make a call to get cluster info
            if (null == clusterInfoList) {
                if (searchAllClustersForStorageVolumes) {
                    clusterInfoList = client.getClusterInfoDetails();
                } else {
                    clusterInfoList = new ArrayList<VPlexClusterInfo>();
                }
            }
            // Make the call to create a virtual volume. It is distributed if there are two (or more?)
            // physical volumes.
            boolean isDistributed = (vinfos.size() >= 2);
            thinEnabled = thinEnabled && verifyVplexSupportsThinProvisioning(vplex);
            VPlexVirtualVolumeInfo vvInfo = client.createVirtualVolume(vinfos, isDistributed, discoveryRequired, false, clusterId, clusterInfoList, false, thinEnabled, searchAllClustersForStorageVolumes);
            // Note: according to client.createVirtualVolume, this will never be the case.
            if (vvInfo == null) {
                VPlexApiException ex = VPlexApiException.exceptions.cantFindRequestedVolume(vplexVolume.getLabel());
                throw ex;
            }
            vplexVolumeNameMap.put(vvInfo.getName(), vplexVolume);
            virtualVolumeInfos.add(vvInfo);
        }
        InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_046);
        Map<String, VPlexVirtualVolumeInfo> foundVirtualVolumes = client.findVirtualVolumes(clusterInfoList, virtualVolumeInfos);
        if (!foundVirtualVolumes.isEmpty()) {
            for (Entry<String, Volume> entry : vplexVolumeNameMap.entrySet()) {
                Volume vplexVolume = entry.getValue();
                VPlexVirtualVolumeInfo vvInfo = foundVirtualVolumes.get(entry.getKey());
                try {
                    // Now we try and rename the volume to the customized name. Note that if custom naming
                    // is disabled the custom name will not be generated and will be null.
                    // Create the VPLEX volume name custom configuration datasource and generate the
                    // custom volume name based on whether the volume is a local or distributed volume.
                    String hostOrClusterName = null;
                    URI computeResourceURI = computeResourceMap.get(vplexVolume.getId());
                    if (computeResourceURI != null) {
                        DataObject hostOrCluster = null;
                        if (URIUtil.isType(computeResourceURI, Cluster.class)) {
                            hostOrCluster = getDataObject(Cluster.class, computeResourceURI, _dbClient);
                        } else if (URIUtil.isType(computeResourceURI, Host.class)) {
                            hostOrCluster = getDataObject(Host.class, computeResourceURI, _dbClient);
                        }
                        if ((hostOrCluster != null) && ((vplexVolume.getPersonality() == null) || (vplexVolume.checkPersonality(Volume.PersonalityTypes.SOURCE)))) {
                            hostOrClusterName = hostOrCluster.getLabel();
                        }
                    }
                    if (CustomVolumeNamingUtils.isCustomVolumeNamingEnabled(customConfigHandler, vplex.getSystemType())) {
                        String customConfigName = CustomVolumeNamingUtils.getCustomConfigName(hostOrClusterName != null);
                        Project project = getDataObject(Project.class, vplexVolume.getProject().getURI(), _dbClient);
                        TenantOrg tenant = getDataObject(TenantOrg.class, vplexVolume.getTenant().getURI(), _dbClient);
                        DataSource customNameDataSource = CustomVolumeNamingUtils.getCustomConfigDataSource(project, tenant, vplexVolume.getLabel(), vvInfo.getWwn(), hostOrClusterName, dataSourceFactory, customConfigName, _dbClient);
                        if (customNameDataSource != null) {
                            String customVolumeName = CustomVolumeNamingUtils.getCustomName(customConfigHandler, customConfigName, customNameDataSource, vplex.getSystemType());
                            vvInfo = CustomVolumeNamingUtils.renameVolumeOnVPlex(vvInfo, customVolumeName, client);
                            // Update the label to match the custom name.
                            vplexVolume.setLabel(vvInfo.getName());
                            // Also, we update the name portion of the project and tenant URIs
                            // to reflect the custom name. This is necessary because the API
                            // to search for volumes by project, extracts the name portion of the
                            // project URI to get the volume name.
                            NamedURI namedURI = vplexVolume.getProject();
                            namedURI.setName(vvInfo.getName());
                            vplexVolume.setProject(namedURI);
                            namedURI = vplexVolume.getTenant();
                            namedURI.setName(vvInfo.getName());
                            vplexVolume.setTenant(namedURI);
                        }
                    }
                } catch (Exception e) {
                    _log.warn(String.format("Error renaming newly created VPLEX volume %s:%s", vplexVolume.getId(), vplexVolume.getLabel()), e);
                }
                buf.append(vvInfo.getName() + " ");
                _log.info(String.format("Created virtual volume: %s path: %s size: %s", vvInfo.getName(), vvInfo.getPath(), vvInfo.getCapacityBytes()));
                vplexVolume.setNativeId(vvInfo.getPath());
                vplexVolume.setNativeGuid(vvInfo.getPath());
                vplexVolume.setDeviceLabel(vvInfo.getName());
                vplexVolume.setThinlyProvisioned(vvInfo.isThinEnabled());
                checkThinEnabledResult(vvInfo, thinEnabled, _workflowService.getWorkflowFromStepId(stepId).getOrchTaskId());
                vplexVolume.setWWN(vvInfo.getWwn());
                // For Vplex virtual volumes set allocated capacity to 0 (cop-18608)
                vplexVolume.setAllocatedCapacity(0L);
                vplexVolume.setProvisionedCapacity(vvInfo.getCapacityBytes());
                _dbClient.updateObject(vplexVolume);
                // Record VPLEX volume created event.
                createdVplexVolumeURIs.add(vplexVolume.getId());
                recordBourneVolumeEvent(vplexVolume.getId(), OperationTypeEnum.CREATE_BLOCK_VOLUME.getEvType(true), Operation.Status.ready, OperationTypeEnum.CREATE_BLOCK_VOLUME.getDescription());
            }
        }
        if (foundVirtualVolumes.size() != vplexVolumeNameMap.size()) {
            VPlexApiException ex = VPlexApiException.exceptions.cantFindAllRequestedVolume();
            throw ex;
        }
        long elapsed = System.currentTimeMillis() - startTime;
        _log.info(String.format("TIMER: %s virtual volume(s) %s create took %f seconds", volumeMap.size(), volumeLabels.toString(), (double) elapsed / (double) 1000));
        WorkflowStepCompleter.stepSucceded(stepId);
    } catch (VPlexApiException vae) {
        _log.error("Exception creating Vplex Virtual Volume: " + vae.getMessage(), vae);
        // not created.
        for (URI vplexVolumeURI : vplexVolumeURIs) {
            if (!createdVplexVolumeURIs.contains(vplexVolumeURI)) {
                recordBourneVolumeEvent(vplexVolumeURI, OperationTypeEnum.CREATE_BLOCK_VOLUME.getEvType(false), Operation.Status.error, OperationTypeEnum.CREATE_BLOCK_VOLUME.getDescription());
            }
        }
        WorkflowStepCompleter.stepFailed(stepId, vae);
    } catch (Exception ex) {
        _log.error("Exception creating Vplex Virtual Volume: " + ex.getMessage(), ex);
        // not created.
        for (URI vplexVolumeURI : vplexVolumeURIs) {
            if (!createdVplexVolumeURIs.contains(vplexVolumeURI)) {
                recordBourneVolumeEvent(vplexVolumeURI, OperationTypeEnum.CREATE_BLOCK_VOLUME.getEvType(false), Operation.Status.error, OperationTypeEnum.CREATE_BLOCK_VOLUME.getDescription());
            }
        }
        String opName = ResourceOperationTypeEnum.CREATE_VIRTUAL_VOLUME.getName();
        ServiceError serviceError = VPlexApiException.errors.createVirtualVolumesFailed(opName, ex);
        WorkflowStepCompleter.stepFailed(stepId, serviceError);
    }
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) StringSet(com.emc.storageos.db.client.model.StringSet) HashMap(java.util.HashMap) NamedURI(com.emc.storageos.db.client.model.NamedURI) ArrayList(java.util.ArrayList) VolumeInfo(com.emc.storageos.vplex.api.clientdata.VolumeInfo) VPlexVirtualVolumeInfo(com.emc.storageos.vplex.api.VPlexVirtualVolumeInfo) NamedURI(com.emc.storageos.db.client.model.NamedURI) URI(java.net.URI) VPlexVirtualVolumeInfo(com.emc.storageos.vplex.api.VPlexVirtualVolumeInfo) VPlexClusterInfo(com.emc.storageos.vplex.api.VPlexClusterInfo) StringSet(com.emc.storageos.db.client.model.StringSet) ApplicationAddVolumeList(com.emc.storageos.volumecontroller.ApplicationAddVolumeList) ArrayList(java.util.ArrayList) URIQueryResultList(com.emc.storageos.db.client.constraint.URIQueryResultList) List(java.util.List) StorageSystem(com.emc.storageos.db.client.model.StorageSystem) HashSet(java.util.HashSet) ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) Cluster(com.emc.storageos.db.client.model.Cluster) Host(com.emc.storageos.db.client.model.Host) 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) DataSource(com.emc.storageos.customconfigcontroller.DataSource) Project(com.emc.storageos.db.client.model.Project) DiscoveredDataObject(com.emc.storageos.db.client.model.DiscoveredDataObject) DataObject(com.emc.storageos.db.client.model.DataObject) VPlexControllerUtils.getDataObject(com.emc.storageos.vplexcontroller.VPlexControllerUtils.getDataObject) Volume(com.emc.storageos.db.client.model.Volume) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient) TenantOrg(com.emc.storageos.db.client.model.TenantOrg)

Example 23 with VPlexApiException

use of com.emc.storageos.vplex.api.VPlexApiException in project coprhd-controller by CoprHD.

the class VPlexDeviceController method migrationSupportedForVolume.

/**
 * Determines if the controller can support migration for the passed VPLEX volume.
 *
 * @param volume
 *            A reference to a VPLEX volume.
 * @param varrayURI
 *            A reference to a varray or null.
 *
 * @return true if migration is supported, false otherwise.
 */
public static boolean migrationSupportedForVolume(Volume volume, URI varrayURI, DbClient dbClient) {
    boolean supported = true;
    // Migration is supported for all volumes that were not ingested.
    if (volume.isIngestedVolumeWithoutBackend(dbClient)) {
        VirtualPool vpool = dbClient.queryObject(VirtualPool.class, volume.getVirtualPool());
        // Migration is supported for all local volumes.
        if (VirtualPool.HighAvailabilityType.vplex_distributed.name().equals(vpool.getHighAvailability())) {
            StorageSystem vplexSystem = dbClient.queryObject(StorageSystem.class, volume.getStorageController());
            try {
                VPlexApiFactory apiFactory = VPlexApiFactory.getInstance();
                VPlexApiClient client = getVPlexAPIClient(apiFactory, vplexSystem, dbClient);
                VPlexVirtualVolumeInfo vvInfo = client.getVirtualVolumeStructure(volume.getDeviceLabel());
                VPlexDistributedDeviceInfo ddInfo = (VPlexDistributedDeviceInfo) vvInfo.getSupportingDeviceInfo();
                List<VPlexDeviceInfo> localDeviceInfoList = ddInfo.getLocalDeviceInfo();
                for (VPlexDeviceInfo localDeviceInfo : localDeviceInfoList) {
                    _log.info("localDeviceInfo: {}, {}", localDeviceInfo.getName(), localDeviceInfo.getCluster());
                    // the passed varray.
                    if (varrayURI != null) {
                        _log.info("varrayURI:{}", varrayURI);
                        String varrayCluster = ConnectivityUtil.getVplexClusterForVarray(varrayURI, vplexSystem.getId(), dbClient);
                        _log.info("varrayCluster:{}", varrayCluster);
                        if (!localDeviceInfo.getCluster().contains(varrayCluster)) {
                            continue;
                        }
                    }
                    // For distributed volumes, the local device must be built
                    // on a single extent.
                    _log.info("Local device: {}", localDeviceInfo.getName());
                    _log.info("Extent count: {}", localDeviceInfo.getExtentInfo().size());
                    if (localDeviceInfo.getExtentInfo().size() != 1) {
                        supported = false;
                        break;
                    }
                }
            } catch (VPlexApiException vae) {
                _log.error("Exception checking if migration supported for volume:", vae);
                throw vae;
            } catch (Exception ex) {
                _log.error("Exception checking if migration supported for volume", ex);
                throw VPlexApiException.exceptions.failedGettingMigrationSupportedForVolume(vplexSystem.getId().toString(), volume.getLabel());
            }
        }
    }
    return supported;
}
Also used : VPlexDistributedDeviceInfo(com.emc.storageos.vplex.api.VPlexDistributedDeviceInfo) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient) VirtualPool(com.emc.storageos.db.client.model.VirtualPool) VPlexDeviceInfo(com.emc.storageos.vplex.api.VPlexDeviceInfo) VPlexVirtualVolumeInfo(com.emc.storageos.vplex.api.VPlexVirtualVolumeInfo) 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) StorageSystem(com.emc.storageos.db.client.model.StorageSystem) VPlexApiFactory(com.emc.storageos.vplex.api.VPlexApiFactory)

Example 24 with VPlexApiException

use of com.emc.storageos.vplex.api.VPlexApiException in project coprhd-controller by CoprHD.

the class VPlexDeviceController method exportGroupRemoveVolumes.

/*
     * (non-Javadoc)
     *
     * @see com.emc.storageos.volumecontroller.impl.vplex.VplexController#exportRemoveVolume(java.net.URI, java.net.URI,
     * java.net.URI,
     * java.lang.String)
     */
@Override
public void exportGroupRemoveVolumes(URI vplexURI, URI exportURI, List<URI> volumeURIs, String opId) throws ControllerException {
    String volListStr = "";
    ExportRemoveVolumeCompleter completer = null;
    boolean hasSteps = false;
    try {
        _log.info("entering export group remove volumes");
        WorkflowStepCompleter.stepExecuting(opId);
        completer = new ExportRemoveVolumeCompleter(exportURI, volumeURIs, opId);
        volListStr = Joiner.on(',').join(volumeURIs);
        validator.volumeURIs(volumeURIs, false, false, ValCk.ID);
        Workflow workflow = _workflowService.getNewWorkflow(this, EXPORT_GROUP_REMOVE_VOLUMES, false, opId);
        StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
        ExportGroup exportGroup = getDataObject(ExportGroup.class, exportURI, _dbClient);
        List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup, vplex.getId());
        StringBuffer errorMessages = new StringBuffer();
        boolean isValidationNeeded = validatorConfig.isValidationEnabled();
        _log.info("Orchestration level validation needed : {}", isValidationNeeded);
        VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
        // For safety, run remove volumes serially
        String previousStep = null;
        for (ExportMask exportMask : exportMasks) {
            if (exportMask.getInactive()) {
                _log.info(String.format("ExportMask %s (%s) is inactive, skipping", exportMask.getMaskName(), exportMask.getId()));
                continue;
            }
            String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
            Map<String, String> targetPortToPwwnMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
            VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
            _log.info("Refreshing ExportMask {}", exportMask.getMaskName());
            VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortToPwwnMap, _networkDeviceController);
            List<URI> volumeURIList = new ArrayList<URI>();
            List<URI> remainingVolumesInMask = new ArrayList<URI>();
            if (exportMask.getVolumes() != null && !exportMask.getVolumes().isEmpty()) {
                // note that this is the assumed behavior even for the
                // situation in which this export mask is in use by other
                // export groups... see CTRL-3941
                // assemble a list of other ExportGroups that reference this ExportMask
                List<ExportGroup> otherExportGroups = ExportUtils.getOtherExportGroups(exportGroup, exportMask, _dbClient);
                if (otherExportGroups != null && !otherExportGroups.isEmpty()) {
                    // Gets the list of volume URIs that are not other Export Groups
                    volumeURIList = getVolumeListDiff(exportGroup, exportMask, otherExportGroups, volumeURIs);
                } else {
                    volumeURIList = volumeURIs;
                }
                Map<URI, BlockObject> blockObjectMap = VPlexUtil.translateRPSnapshots(_dbClient, volumeURIList);
                volumeURIList.clear();
                volumeURIList.addAll(blockObjectMap.keySet());
                volumeURIList = ExportMaskUtils.filterVolumesByExportMask(volumeURIList, exportMask);
                for (String volumeURI : exportMask.getVolumes().keySet()) {
                    remainingVolumesInMask.add(URI.create(volumeURI));
                }
                remainingVolumesInMask.removeAll(volumeURIList);
            }
            _log.info(String.format("exportGroupRemove: mask %s volumes to process: %s", exportMask.getMaskName(), volumeURIList.toString()));
            // Fetch exportMask again as exportMask zoning Map might have changed in zoneExportRemoveVolumes
            exportMask = _dbClient.queryObject(ExportMask.class, exportMask.getId());
            boolean existingVolumes = exportMask.hasAnyExistingVolumes();
            // The useradded - existing initiators enhancement made in refreshExportMask
            // guarantees that all the discovered initiators are added to userAdded,
            // irrespective of whether it's already registered on the array manually.
            boolean existingInitiators = exportMask.hasAnyExistingInitiators();
            boolean canMaskBeDeleted = remainingVolumesInMask.isEmpty() && !existingInitiators && !existingVolumes;
            if (!canMaskBeDeleted) {
                _log.info("this mask is not empty, so just updating: " + exportMask.getMaskName());
                completer.addExportMaskToRemovedVolumeMapping(exportMask.getId(), volumeURIList);
                Workflow.Method storageViewRemoveVolume = storageViewRemoveVolumesMethod(vplex.getId(), exportGroup.getId(), exportMask.getId(), volumeURIList, opId, completer, null);
                previousStep = workflow.createStep("removeVolumes", String.format("Removing volumes from export on storage array %s (%s) for export mask %s (%s)", vplex.getNativeGuid(), vplex.getId().toString(), exportMask.getMaskName(), exportMask.getId()), previousStep, vplex.getId(), vplex.getSystemType(), this.getClass(), storageViewRemoveVolume, rollbackMethodNullMethod(), null);
                // Add zoning step for removing volumes
                List<NetworkZoningParam> zoningParam = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), Collections.singletonList(exportMask.getId()), _dbClient);
                Workflow.Method zoneRemoveVolumesMethod = _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParam, volumeURIList);
                previousStep = workflow.createStep(null, "Zone remove volumes mask: " + exportMask.getMaskName(), previousStep, nullURI, "network-system", _networkDeviceController.getClass(), zoneRemoveVolumesMethod, _networkDeviceController.zoneNullRollbackMethod(), null);
                hasSteps = true;
            /**
             * TODO
             * ExportremoveVolumeCompleter should be enhanced to remove export mask from export group if last volume removed.
             * We already take care of this.
             */
            } else {
                _log.info("this mask is empty of ViPR-managed volumes, and there are no existing volumes or initiators, so deleting: " + exportMask.getMaskName());
                List<NetworkZoningParam> zoningParams = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportURI, Collections.singletonList(exportMask.getId()), _dbClient);
                hasSteps = true;
                String exportMaskDeleteStep = workflow.createStepId();
                Workflow.Method deleteStorageView = deleteStorageViewMethod(vplexURI, exportURI, exportMask.getId(), false);
                previousStep = workflow.createStep(DELETE_STORAGE_VIEW, String.format("Deleting storage view: %s (%s)", exportMask.getMaskName(), exportMask.getId()), previousStep, vplexURI, vplex.getSystemType(), this.getClass(), deleteStorageView, null, exportMaskDeleteStep);
                // Unzone step (likely just a FCZoneReference removal since this is remove volume only)
                previousStep = workflow.createStep(ZONING_STEP, "Zoning subtask for export-group: " + exportURI, previousStep, NullColumnValueGetter.getNullURI(), "network-system", _networkDeviceController.getClass(), _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParams, volumeURIs), null, workflow.createStepId());
            }
        }
        if (hasSteps) {
            String message = errorMessages.toString();
            if (isValidationNeeded && !message.isEmpty()) {
                _log.error("Error Message {}", errorMessages);
                List<Volume> volumes = _dbClient.queryObject(Volume.class, volumeURIs);
                String volumesForDisplay = Joiner.on(", ").join(Collections2.transform(volumes, CommonTransformerFunctions.fctnDataObjectToForDisplay()));
                throw DeviceControllerException.exceptions.removeVolumesValidationError(volumesForDisplay, vplex.forDisplay(), message);
            }
            workflow.executePlan(completer, String.format("Sucessfully removed volumes or deleted Storage View: %s (%s)", exportGroup.getLabel(), exportGroup.getId()));
        } else {
            completer.ready(_dbClient);
        }
    } catch (VPlexApiException vae) {
        String message = String.format("Failed to remove Volume %s from ExportGroup %s", volListStr, exportURI);
        _log.error(message, vae);
        failStep(completer, opId, vae);
    } catch (Exception ex) {
        String message = String.format("Failed to remove Volume %s from ExportGroup %s", volListStr, exportURI);
        _log.error(message, ex);
        String opName = ResourceOperationTypeEnum.DELETE_EXPORT_VOLUME.getName();
        ServiceError serviceError = VPlexApiException.errors.exportGroupRemoveVolumesFailed(volListStr, exportURI.toString(), opName, ex);
        failStep(completer, opId, serviceError);
    }
}
Also used : VPlexStorageViewInfo(com.emc.storageos.vplex.api.VPlexStorageViewInfo) ArrayList(java.util.ArrayList) NamedURI(com.emc.storageos.db.client.model.NamedURI) URI(java.net.URI) BlockObject(com.emc.storageos.db.client.model.BlockObject) StorageSystem(com.emc.storageos.db.client.model.StorageSystem) ServiceError(com.emc.storageos.svcs.errorhandling.model.ServiceError) ExportMask(com.emc.storageos.db.client.model.ExportMask) Workflow(com.emc.storageos.workflow.Workflow) 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) NetworkZoningParam(com.emc.storageos.networkcontroller.impl.NetworkZoningParam) ExportRemoveVolumeCompleter(com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportRemoveVolumeCompleter) ExportGroup(com.emc.storageos.db.client.model.ExportGroup) Volume(com.emc.storageos.db.client.model.Volume) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient)

Example 25 with VPlexApiException

use of com.emc.storageos.vplex.api.VPlexApiException in project coprhd-controller by CoprHD.

the class VPlexDeviceController method rollbackRestoreResync.

/**
 * Called if the restore/resync volume operation fails
 *
 * @param vplexURI
 *            The URI of the VPLEX system.
 * @param vplexVolumeURI
 *            The URI of the distributed VPLEX volume.
 * @param cgURI
 *            The URI of the volume's CG or null.
 * @param detachStepId
 *            The Id of the detach mirror step.
 * @param stepId
 *            The workflow step identifier.
 */
public void rollbackRestoreResync(URI vplexURI, URI vplexVolumeURI, URI cgURI, String detachStepId, String stepId) {
    _log.info("Executing rollback of restore/resync volume {} on VPLEX {}", new Object[] { vplexVolumeURI, vplexURI });
    String mirrorDeviceName = "";
    try {
        // Update workflow step.
        WorkflowStepCompleter.stepExecuting(stepId);
        // Get the rollback data so we know what to do.
        @SuppressWarnings("unchecked") Map<String, String> rollbackData = (Map<String, String>) _workflowService.loadStepData(detachStepId);
        String successWarningMessage = null;
        if (rollbackData != null) {
            boolean reattachMirror = Boolean.parseBoolean(rollbackData.get(REATTACH_MIRROR));
            boolean addVolumeBackToCG = Boolean.parseBoolean(rollbackData.get(ADD_BACK_TO_CG));
            String restoreDeviceName = rollbackData.get(RESTORE_DEVICE_NAME);
            if ((restoreDeviceName != null) || reattachMirror || addVolumeBackToCG) {
                // Get the API client.
                StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
                VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexSystem, _dbClient);
                _log.info("Got VPLEX API client");
                // Get the VPLEX volume.
                Volume vplexVolume = getDataObject(Volume.class, vplexVolumeURI, _dbClient);
                String vplexVolumeName = vplexVolume.getDeviceLabel();
                _log.info("Got VPLEX volume");
                // the remote mirror, do this first.
                if (reattachMirror) {
                    // Attach the mirror.
                    mirrorDeviceName = rollbackData.get(DETACHED_DEVICE);
                    client.reattachMirrorToDistributedVolume(vplexVolumeName, mirrorDeviceName);
                    _log.info("Reattached the mirror");
                }
                // and needs to be restored, do so after reattaching the device.
                if (restoreDeviceName != null) {
                    String modifiedName = null;
                    try {
                        modifiedName = restoreDeviceName.substring(0, VPlexApiConstants.MAX_DEVICE_NAME_LENGTH_FOR_ATTACH_MIRROR);
                        client.renameDistributedDevice(modifiedName, restoreDeviceName);
                    } catch (Exception e) {
                        // We don't fail the rollback workflow step in this case, but instead just log a message
                        // indicating this error. The distribute device for the volume will just have
                        // the modified name.
                        successWarningMessage = String.format("Failed renaming the distributed device %s back " + " to its orginal name %s after reattaching remote mirror", modifiedName, restoreDeviceName);
                        _log.warn(successWarningMessage);
                    }
                }
                // reattached.
                if (addVolumeBackToCG) {
                    ConsistencyGroupManager consistencyGroupManager = getConsistencyGroupManager(vplexVolume);
                    consistencyGroupManager.addVolumeToCg(cgURI, vplexVolume, client, false);
                    _log.info("Added volume back to consistency group.");
                }
            }
        }
        // Update workflow step state to success.
        if (successWarningMessage != null) {
            WorkflowStepCompleter.stepSucceeded(stepId, successWarningMessage);
        } else {
            WorkflowStepCompleter.stepSucceded(stepId);
        }
        _log.info("Updated workflow step state to success");
    } catch (VPlexApiException vae) {
        _log.error("Exception in restore/resync volume rollback for VPLEX distributed volume" + vae.getMessage(), vae);
        WorkflowStepCompleter.stepFailed(stepId, vae);
    } catch (Exception e) {
        _log.error("Exception in restore/resync volume rollback for VPLEX distributed volume " + e.getMessage(), e);
        WorkflowStepCompleter.stepFailed(stepId, VPlexApiException.exceptions.failedAttachingVPlexVolumeMirror(mirrorDeviceName, vplexVolumeURI.toString(), e));
    }
}
Also used : Volume(com.emc.storageos.db.client.model.Volume) VPlexApiException(com.emc.storageos.vplex.api.VPlexApiException) VPlexApiClient(com.emc.storageos.vplex.api.VPlexApiClient) Map(java.util.Map) OpStatusMap(com.emc.storageos.db.client.model.OpStatusMap) HashMap(java.util.HashMap) StringMap(com.emc.storageos.db.client.model.StringMap) 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) StorageSystem(com.emc.storageos.db.client.model.StorageSystem)

Aggregations

VPlexApiException (com.emc.storageos.vplex.api.VPlexApiException)59 StorageSystem (com.emc.storageos.db.client.model.StorageSystem)53 ServiceError (com.emc.storageos.svcs.errorhandling.model.ServiceError)43 InternalException (com.emc.storageos.svcs.errorhandling.resources.InternalException)43 ControllerException (com.emc.storageos.volumecontroller.ControllerException)43 WorkflowException (com.emc.storageos.workflow.WorkflowException)43 DeviceControllerException (com.emc.storageos.exceptions.DeviceControllerException)41 InternalServerErrorException (com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException)41 VPlexApiClient (com.emc.storageos.vplex.api.VPlexApiClient)40 URI (java.net.URI)39 ArrayList (java.util.ArrayList)39 DatabaseException (com.emc.storageos.db.exceptions.DatabaseException)34 IOException (java.io.IOException)34 URISyntaxException (java.net.URISyntaxException)34 ExportMask (com.emc.storageos.db.client.model.ExportMask)26 Volume (com.emc.storageos.db.client.model.Volume)25 NamedURI (com.emc.storageos.db.client.model.NamedURI)21 Initiator (com.emc.storageos.db.client.model.Initiator)20 BlockStorageDevice (com.emc.storageos.volumecontroller.BlockStorageDevice)14 VPlexVirtualVolumeInfo (com.emc.storageos.vplex.api.VPlexVirtualVolumeInfo)12