/** * Updates the target LUN ID for volumes in the export mask. * * @param volumeToTargetLunMap * @param exportMask */ private void updateTargetLunIdInExportMask( Map<URI, Integer> volumeToTargetLunMap, ExportMask exportMask) { for (URI volumeURI : volumeToTargetLunMap.keySet()) { Integer targetLunId = volumeToTargetLunMap.get(volumeURI); exportMask.getVolumes().put(volumeURI.toString(), targetLunId.toString()); } }
@Override public void exportGroupRemoveInitiators( URI storageURI, URI exportGroupURI, List<URI> initiatorURIs, String token) throws Exception { /* * foreach ScaleOI volume in ExportGroup * foreach initiator in list * if volume not used in another ExportGroup with same initiator * scli unmap --volume volid --sdc initiator.sdcid */ ExportOrchestrationTask taskCompleter = new ExportOrchestrationTask(exportGroupURI, token); try { ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI); StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI); if (initiatorURIs != null && !initiatorURIs.isEmpty() && exportGroup.getExportMasks() != null) { // Set up workflow steps. Workflow workflow = _workflowService.getNewWorkflow( MaskingWorkflowEntryPoints.getInstance(), "exportGroupRemoveInitiators", true, token); // Create a mapping of ExportMask URI to initiators to remove Map<URI, List<URI>> exportToInitiatorsToRemove = new HashMap<>(); Map<URI, List<URI>> exportToVolumesToRemove = new HashMap<>(); Map<URI, Integer> volumeMap = null; for (String exportMaskURIStr : exportGroup.getExportMasks()) { URI exportMaskURI = URI.create(exportMaskURIStr); ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); if (exportMask == null) { continue; } for (URI initiatorURI : initiatorURIs) { Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI); if (initiator == null || !exportMask.hasInitiator(initiatorURI.toString())) { continue; } if (ExportUtils.getInitiatorExportGroups(initiator, _dbClient).size() == 1) { List<URI> initiators = exportToInitiatorsToRemove.get(exportGroupURI); if (initiators == null) { initiators = new ArrayList<>(); exportToInitiatorsToRemove.put(exportMaskURI, initiators); } initiators.add(initiatorURI); } else { if (volumeMap == null) { volumeMap = ExportUtils.getExportGroupVolumeMap(_dbClient, storage, exportGroup); } List<URI> volumeURIs = exportToVolumesToRemove.get(exportGroupURI); if (volumeURIs == null) { volumeURIs = new ArrayList<>(); exportToVolumesToRemove.put(exportMaskURI, volumeURIs); } for (URI volumeURI : volumeMap.keySet()) { // Only add to the remove list for the ExportMask if // the EM is not being shared with another ExportGroup Integer count = ExportUtils.getNumberOfExportGroupsWithVolume(initiator, volumeURI, _dbClient); if (count == 1) { volumeURIs.add(volumeURI); } } } } } // Generate the remove initiators steps for the entries that were determined above for (Map.Entry<URI, List<URI>> toRemoveInits : exportToInitiatorsToRemove.entrySet()) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, toRemoveInits.getKey()); if (exportMask != null) { List<URI> removeInitURIs = toRemoveInits.getValue(); List<String> exportMaskInitiatorURIs = new ArrayList<>(exportMask.getInitiators()); for (URI uri : removeInitURIs) { exportMaskInitiatorURIs.remove(uri.toString()); } if (exportMaskInitiatorURIs.isEmpty()) { log.info( String.format("Adding step to delete ExportMask %s", exportMask.getMaskName())); generateExportMaskDeleteWorkflow( workflow, null, storage, exportGroup, exportMask, null); } else { log.info( String.format( "Adding step to remove initiators %s from ExportMask %s", Joiner.on(',').join(removeInitURIs), exportMask.getMaskName())); generateExportMaskRemoveInitiatorsWorkflow( workflow, null, storage, exportGroup, exportMask, removeInitURIs, true); } } } // Generate the remove volume for those cases where we remove initiators // from an ExportGroup that contains more than one host/initiator for (Map.Entry<URI, List<URI>> toRemoveVols : exportToVolumesToRemove.entrySet()) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, toRemoveVols.getKey()); List<URI> removeVolumeURIs = toRemoveVols.getValue(); if (exportMask != null && !removeVolumeURIs.isEmpty()) { List<String> exportMaskVolumeURIs = new ArrayList<>(exportMask.getVolumes().keySet()); for (URI uri : removeVolumeURIs) { exportMaskVolumeURIs.remove(uri.toString()); } if (exportMaskVolumeURIs.isEmpty()) { log.info( String.format("Adding step to delete ExportMask %s", exportMask.getMaskName())); generateExportMaskDeleteWorkflow( workflow, null, storage, exportGroup, exportMask, null); } else { log.info( String.format( "Adding step to remove volumes %s from ExportMask %s", Joiner.on(',').join(removeVolumeURIs), exportMask.getMaskName())); generateExportMaskRemoveVolumesWorkflow( workflow, null, storage, exportGroup, exportMask, removeVolumeURIs, null); } } } String successMessage = String.format( "ExportGroup remove initiators successfully applied for StorageArray %s", storage.getLabel()); workflow.executePlan(taskCompleter, successMessage); } else { taskCompleter.ready(_dbClient); } } catch (DeviceControllerException dex) { taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupRemoveInitiators", dex.getMessage())); } catch (Exception ex) { _log.error("ExportGroup Orchestration failed.", ex); taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupRemoveInitiators", ex.getMessage())); } }
@Override public void deleteOrRemoveVolumesFromExportMask( URI arrayURI, URI exportGroupURI, URI exportMaskURI, List<URI> volumes, List<URI> initiatorURIs, TaskCompleter completer, String stepId) { try { StorageSystem array = _dbClient.queryObject(StorageSystem.class, arrayURI); BlockStorageDevice device = _blockController.getDevice(array.getSystemType()); ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); WorkflowStepCompleter.stepExecuting(stepId); // If the exportMask isn't found, or has been deleted, nothing to do. if (exportMask == null || exportMask.getInactive()) { _log.info(String.format("ExportMask %s inactive, returning success", exportMaskURI)); WorkflowStepCompleter.stepSucceded(stepId); return; } // Protect concurrent operations by locking {host, array} dupple. // Lock will be released when workflow step completes. List<String> lockKeys = ControllerLockingUtil.getHostStorageLockKeys( _dbClient, ExportGroupType.Host, StringSetUtil.stringSetToUriList(exportMask.getInitiators()), arrayURI); getWorkflowService() .acquireWorkflowStepLocks( stepId, lockKeys, LockTimeoutValue.get(LockType.VPLEX_BACKEND_EXPORT)); // Make sure the completer will complete the workflow. This happens on rollback case. if (!completer.getOpId().equals(stepId)) { completer.setOpId(stepId); } // Refresh the ExportMask exportMask = refreshExportMask(array, device, exportMask); // Determine if we're deleting the last volume in the mask. StringMap maskVolumesMap = exportMask.getVolumes(); Set<String> remainingVolumes = new HashSet<String>(); List<URI> passedVolumesInMask = new ArrayList<>(volumes); if (maskVolumesMap != null) { remainingVolumes.addAll(maskVolumesMap.keySet()); } for (URI volume : volumes) { remainingVolumes.remove(volume.toString()); // Remove any volumes from the volume list that are no longer // in the export mask. When a failure occurs removing a backend // volume from a mask, the rollback method will try and remove it // again. However, in the case of a distributed volume, one side // may have succeeded, so we will try and remove it again. Previously, // this was not a problem. However, new validation exists at the // block level that checks to make sure the volume to remove is // actually in the mask, which now causes a failure when you remove // it a second time. So, we check here and remove any volumes that // are not in the mask to handle this condition. if ((maskVolumesMap != null) && (!maskVolumesMap.keySet().contains(volume.toString()))) { passedVolumesInMask.remove(volume); } } // None of the volumes is in the export mask, so we are done. if (passedVolumesInMask.isEmpty()) { _log.info( "None of these volumes {} are in export mask {}", volumes, exportMask.forDisplay()); WorkflowStepCompleter.stepSucceded(stepId); return; } // If it is last volume and there are no existing volumes, delete the ExportMask. if (remainingVolumes.isEmpty() && !exportMask.hasAnyExistingVolumes()) { device.doExportDelete(array, exportMask, passedVolumesInMask, initiatorURIs, completer); } else { List<Initiator> initiators = null; if (initiatorURIs != null && !initiatorURIs.isEmpty()) { initiators = _dbClient.queryObject(Initiator.class, initiatorURIs); } device.doExportRemoveVolumes(array, exportMask, passedVolumesInMask, initiators, completer); } } catch (Exception ex) { _log.error("Failed to delete or remove volumes to export mask for vnx: ", ex); VPlexApiException vplexex = DeviceControllerExceptions.vplex.addStepsForCreateVolumesFailed(ex); WorkflowStepCompleter.stepFailed(stepId, vplexex); } }
@Override public void deleteOrRemoveVolumesFromExportMask( URI arrayURI, URI exportGroupURI, URI exportMaskURI, List<URI> volumes, TaskCompleter completer, String stepId) { try { WorkflowStepCompleter.stepExecuting(stepId); StorageSystem array = _dbClient.queryObject(StorageSystem.class, arrayURI); BlockStorageDevice device = _blockController.getDevice(array.getSystemType()); ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); // If the exportMask isn't found, or has been deleted, nothing to do. if (exportMask == null || exportMask.getInactive()) { _log.info(String.format("ExportMask %s inactive, returning success", exportMaskURI)); WorkflowStepCompleter.stepSucceded(stepId); return; } // Protect concurrent operations by locking {host, array} dupple. // Lock will be released when work flow step completes. List<String> lockKeys = ControllerLockingUtil.getHostStorageLockKeys( _dbClient, ExportGroupType.Host, StringSetUtil.stringSetToUriList(exportMask.getInitiators()), arrayURI); getWorkflowService() .acquireWorkflowStepLocks( stepId, lockKeys, LockTimeoutValue.get(LockType.VPLEX_BACKEND_EXPORT)); // Make sure the completer will complete the work flow. This happens on roll back case. if (!completer.getOpId().equals(stepId)) { completer.setOpId(stepId); } // Refresh the ExportMask exportMask = refreshExportMask(array, device, exportMask); // Determine if we're deleting the last volume. Set<String> remainingVolumes = new HashSet<String>(); if (exportMask.getVolumes() != null) { remainingVolumes.addAll(exportMask.getVolumes().keySet()); } for (URI volume : volumes) { remainingVolumes.remove(volume.toString()); } // If it is last volume, delete the ExportMask. if (remainingVolumes.isEmpty() && (exportMask.getExistingVolumes() == null || exportMask.getExistingVolumes().isEmpty())) { _log.debug( String.format( "Calling doExportGroupDelete on the device %s", array.getId().toString())); device.doExportGroupDelete(array, exportMask, completer); } else { _log.debug( String.format( "Calling doExportRemoveVolumes on the device %s", array.getId().toString())); device.doExportRemoveVolumes(array, exportMask, volumes, completer); } } catch (Exception ex) { _log.error("Failed to delete or remove volumes to export mask for cinder: ", ex); VPlexApiException vplexex = DeviceControllerExceptions.vplex.addStepsForDeleteVolumesFailed(ex); WorkflowStepCompleter.stepFailed(stepId, vplexex); } }