@Override public void exportGroupDelete(URI storageURI, URI exportGroupURI, String token) throws Exception { ExportOrchestrationTask taskCompleter = new ExportOrchestrationTask(exportGroupURI, token); try { ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI); StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI); List<ExportMask> masks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup, storageURI); if (masks != null && !masks.isEmpty()) { Workflow workflow = _workflowService.getNewWorkflow( MaskingWorkflowEntryPoints.getInstance(), "exportGroupDelete", true, token); Map<URI, Integer> volumeMap = ExportUtils.getExportGroupVolumeMap(_dbClient, storage, exportGroup); List<URI> volumeURIs = new ArrayList<>(volumeMap.keySet()); List<URI> initiatorURIs = StringSetUtil.stringSetToUriList(exportGroup.getInitiators()); Map<URI, Map<URI, Integer>> exportMaskToVolumeCount = ExportMaskUtils.mapExportMaskToVolumeShareCount(_dbClient, volumeURIs, initiatorURIs); for (ExportMask exportMask : masks) { List<URI> exportGroupURIs = new ArrayList<>(); if (!ExportUtils.isExportMaskShared(_dbClient, exportMask.getId(), exportGroupURIs)) { log.info( String.format("Adding step to delete ExportMask %s", exportMask.getMaskName())); generateExportMaskDeleteWorkflow( workflow, null, storage, exportGroup, exportMask, null); } else { Map<URI, Integer> volumeToExportGroupCount = exportMaskToVolumeCount.get(exportMask.getId()); List<URI> volumesToRemove = new ArrayList<>(); for (URI uri : volumeMap.keySet()) { if (volumeToExportGroupCount == null) { continue; } // Remove the volume only if it is not shared with // more than 1 ExportGroup Integer numExportGroupsVolumeIsIn = volumeToExportGroupCount.get(uri); if (numExportGroupsVolumeIsIn != null && numExportGroupsVolumeIsIn == 1) { volumesToRemove.add(uri); } } if (!volumesToRemove.isEmpty()) { log.info( String.format( "Adding step to remove volumes %s from ExportMask %s", Joiner.on(',').join(volumesToRemove), exportMask.getMaskName())); generateExportMaskRemoveVolumesWorkflow( workflow, null, storage, exportGroup, exportMask, volumesToRemove, null); } } } String successMessage = String.format( "ExportGroup delete successfully completed for StorageArray %s", storage.getLabel()); workflow.executePlan(taskCompleter, successMessage); } else { taskCompleter.ready(_dbClient); } } catch (DeviceControllerException dex) { taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupDelete", dex.getMessage())); } catch (Exception ex) { _log.error("ExportGroup Orchestration failed.", ex); taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupDelete", ex.getMessage())); } }
@Override public void exportGroupAddVolumes( URI storageURI, URI exportGroupURI, Map<URI, Integer> volumeMap, String token) throws Exception { /* * foreach volume in list * foreach initiator in ExportGroup * scli map --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); List<URI> initiatorURIs = StringSetUtil.stringSetToUriList(exportGroup.getInitiators()); if (initiatorURIs != null && !initiatorURIs.isEmpty()) { // Set up workflow steps. Workflow workflow = _workflowService.getNewWorkflow( MaskingWorkflowEntryPoints.getInstance(), "exportGroupAddVolumes", true, token); // Create a mapping of ExportMasks to Add Volumes to or // add to a list of new Exports to create Map<URI, Map<URI, Integer>> exportMaskToVolumesToAdd = new HashMap<>(); List<URI> initiatorsToPlace = new ArrayList<>(initiatorURIs); // Need to figure out which ExportMasks to add volumes to. for (ExportMask exportMask : ExportMaskUtils.getExportMasks(_dbClient, exportGroup, storageURI)) { if (exportMask.hasAnyInitiators()) { exportMaskToVolumesToAdd.put(exportMask.getId(), volumeMap); for (String uriString : exportMask.getInitiators()) { URI initiatorURI = URI.create(uriString); initiatorsToPlace.remove(initiatorURI); } } } Map<String, List<URI>> computeResourceToInitiators = mapInitiatorsToComputeResource(exportGroup, initiatorsToPlace); log.info( String.format( "Need to create ExportMasks for these compute resources %s", Joiner.on(',').join(computeResourceToInitiators.entrySet()))); // ExportMask that need to be newly create because we just added // volumes from 'storage' StorageSystem to this ExportGroup for (Map.Entry<String, List<URI>> toCreate : computeResourceToInitiators.entrySet()) { generateExportMaskCreateWorkflow( workflow, null, storage, exportGroup, toCreate.getValue(), volumeMap, token); } log.info( String.format( "Need to add volumes for these ExportMasks %s", exportMaskToVolumesToAdd.entrySet())); // We already know about the ExportMask, so we just add volumes to it for (Map.Entry<URI, Map<URI, Integer>> toAddVolumes : exportMaskToVolumesToAdd.entrySet()) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, toAddVolumes.getKey()); generateExportMaskAddVolumesWorkflow( workflow, null, storage, exportGroup, exportMask, toAddVolumes.getValue()); } String successMessage = String.format( "ExportGroup add volumes successfully applied for StorageArray %s", storage.getLabel()); workflow.executePlan(taskCompleter, successMessage); } else { taskCompleter.ready(_dbClient); } } catch (DeviceControllerException dex) { taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupAddVolumes", dex.getMessage())); } catch (Exception ex) { _log.error("ExportGroup Orchestration failed.", ex); taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupAddVolumes", ex.getMessage())); } }
@Override public void exportGroupRemoveVolumes( URI storageURI, URI exportGroupURI, List<URI> volumeURIs, String token) throws Exception { /* * foreach volume in list * foreach initiator in ExportGroup * 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); List<ExportMask> masks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup, storageURI); if (masks != null && !masks.isEmpty()) { // Set up workflow steps. Workflow workflow = _workflowService.getNewWorkflow( MaskingWorkflowEntryPoints.getInstance(), "exportGroupRemoveVolumes", true, token); // Generate a list of Initiators List<URI> initiatorURIs = StringSetUtil.stringSetToUriList(exportGroup.getInitiators()); Map<URI, List<URI>> exportToRemoveVolumesList = new HashMap<>(); // Generate a mapping of volume URIs to the # of // ExportGroups that it is associated with Map<URI, Map<URI, Integer>> exportMaskToVolumeCount = ExportMaskUtils.mapExportMaskToVolumeShareCount(_dbClient, volumeURIs, initiatorURIs); // Generate a mapping of the ExportMask URI to a list volumes to // remove from that ExportMask for (ExportMask exportMask : masks) { Map<URI, Integer> volumeToCountMap = exportMaskToVolumeCount.get(exportMask.getId()); if (volumeToCountMap == null) { continue; } for (Map.Entry<URI, Integer> it : volumeToCountMap.entrySet()) { URI volumeURI = it.getKey(); Integer numberOfExportGroupsVolumesIsIn = it.getValue(); if (numberOfExportGroupsVolumesIsIn == 1) { List<URI> volumesToRemove = exportToRemoveVolumesList.get(exportMask.getId()); if (volumesToRemove == null) { volumesToRemove = new ArrayList<>(); exportToRemoveVolumesList.put(exportMask.getId(), volumesToRemove); } volumesToRemove.add(volumeURI); } } } // With the mapping of ExportMask to list of volume URIs, // generate a step to remove the volumes from the ExportMask for (Map.Entry<URI, List<URI>> entry : exportToRemoveVolumesList.entrySet()) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, entry.getKey()); log.info( String.format( "Adding step to remove volumes %s from ExportMask %s", Joiner.on(',').join(entry.getValue()), exportMask.getMaskName())); generateExportMaskRemoveVolumesWorkflow( workflow, null, storage, exportGroup, exportMask, entry.getValue(), null); } String successMessage = String.format( "ExportGroup remove volumes successfully applied for StorageArray %s", storage.getLabel()); workflow.executePlan(taskCompleter, successMessage); } else { taskCompleter.ready(_dbClient); } } catch (DeviceControllerException dex) { taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupRemoveVolumes", dex.getMessage())); } catch (Exception ex) { _log.error("ExportGroup Orchestration failed.", ex); taskCompleter.error( _dbClient, DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation( "exportGroupRemoveVolumes", ex.getMessage())); } }
@Override public void exportGroupAddVolumes( URI storageURI, URI exportGroupURI, Map<URI, Integer> volumeMap, String token) throws Exception { ExportOrchestrationTask taskCompleter = null; try { BlockStorageDevice device = getDevice(); taskCompleter = new ExportOrchestrationTask(exportGroupURI, token); StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI); ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI); boolean anyVolumesAdded = false; boolean createdNewMask = false; if (exportGroup.getExportMasks() != null) { // Set up workflow steps. Workflow workflow = _workflowService.getNewWorkflow( MaskingWorkflowEntryPoints.getInstance(), "exportGroupAddVolumes", true, token); List<ExportMask> exportMasksToZoneAddVolumes = new ArrayList<ExportMask>(); List<URI> volumesToZoneAddVolumes = new ArrayList<URI>(); List<URI> exportMasksToZoneCreate = new ArrayList<URI>(); Map<URI, Integer> volumesToZoneCreate = new HashMap<URI, Integer>(); // Add the volume to all the ExportMasks that are contained in the // ExportGroup. The volumes should be added only if they don't // already exist for the ExportMask. Collection<URI> initiatorURIs = Collections2.transform( exportGroup.getInitiators(), CommonTransformerFunctions.FCTN_STRING_TO_URI); List<URI> hostURIs = new ArrayList<URI>(); Map<String, URI> portNameToInitiatorURI = new HashMap<String, URI>(); List<String> portNames = new ArrayList<String>(); processInitiators(exportGroup, initiatorURIs, portNames, portNameToInitiatorURI, hostURIs); // We always want to have the full list of initiators for the hosts involved in // this export. This will allow the export operation to always find any // existing exports for a given host. queryHostInitiatorsAndAddToList(portNames, portNameToInitiatorURI, initiatorURIs, hostURIs); Map<String, Set<URI>> foundMatches = device.findExportMasks(storage, portNames, false); Set<String> checkMasks = mergeWithExportGroupMaskURIs(exportGroup, foundMatches.values()); for (String maskURIStr : checkMasks) { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, URI.create(maskURIStr)); _log.info(String.format("Checking mask %s", exportMask.getMaskName())); if (!exportMask.getInactive() && exportMask.getStorageDevice().equals(storageURI)) { exportMask = device.refreshExportMask(storage, exportMask); // BlockStorageDevice level, so that it has up-to-date // info from the array Map<URI, Integer> volumesToAdd = getVolumesToAdd(volumeMap, exportMask, exportGroup, token); // Not able to get VolumesToAdd due to error condition so, return if (null == volumesToAdd) { return; } _log.info( String.format( "Mask %s, adding volumes %s", exportMask.getMaskName(), Joiner.on(',').join(volumesToAdd.entrySet()))); if (volumesToAdd.size() > 0) { exportMasksToZoneAddVolumes.add(exportMask); volumesToZoneAddVolumes.addAll(volumesToAdd.keySet()); // Make sure the zoning map is getting updated for user-created masks updateZoningMap(exportGroup, exportMask, true); generateExportMaskAddVolumesWorkflow( workflow, EXPORT_GROUP_ZONING_TASK, storage, exportGroup, exportMask, volumesToAdd); anyVolumesAdded = true; // Need to check if the mask is not already associated with // ExportGroup. This is case when we are adding volume to // the export and there is an existing export on the array. // We have to reuse that existing export, but we need also // associated it with the ExportGroup. if (!exportGroup.hasMask(exportMask.getId())) { exportGroup.addExportMask(exportMask.getId()); _dbClient.updateAndReindexObject(exportGroup); } } } } if (!anyVolumesAdded) { // This is the case where we were requested to add volumes to the // export for this storage array, but none were scheduled to be // added. This could be either because there are existing masks, // but the volumes are already in the export mask or there are no // masks for the storage array. We are checking if there are any // masks and if there are initiators for the export. if (!ExportMaskUtils.hasExportMaskForStorage(_dbClient, exportGroup, storageURI) && exportGroup.hasInitiators()) { _log.info( "No existing masks to which the requested volumes can be added. Creating a new mask"); List<URI> initiators = StringSetUtil.stringSetToUriList(exportGroup.getInitiators()); Map<String, List<URI>> hostInitiatorMap = mapInitiatorsToComputeResource(exportGroup, initiators); if (!hostInitiatorMap.isEmpty()) { for (Map.Entry<String, List<URI>> resourceEntry : hostInitiatorMap.entrySet()) { String computeKey = resourceEntry.getKey(); List<URI> computeInitiatorURIs = resourceEntry.getValue(); _log.info(String.format("New export masks for %s", computeKey)); GenExportMaskCreateWorkflowResult result = generateExportMaskCreateWorkflow( workflow, EXPORT_GROUP_ZONING_TASK, storage, exportGroup, computeInitiatorURIs, volumeMap, token); exportMasksToZoneCreate.add(result.getMaskURI()); volumesToZoneCreate.putAll(volumeMap); } createdNewMask = true; } } } if (!exportMasksToZoneAddVolumes.isEmpty()) { generateZoningAddVolumesWorkflow( workflow, null, exportGroup, exportMasksToZoneAddVolumes, volumesToZoneAddVolumes); } if (!exportMasksToZoneCreate.isEmpty()) { generateZoningCreateWorkflow( workflow, null, exportGroup, exportMasksToZoneCreate, volumesToZoneCreate); } String successMessage = String.format( "Successfully added volumes to export on StorageArray %s", storage.getLabel()); workflow.executePlan(taskCompleter, successMessage); } else { if (exportGroup.hasInitiators()) { _log.info("There are no masks for this export. Need to create anew."); List<URI> initiatorURIs = new ArrayList<URI>(); for (String initiatorURIStr : exportGroup.getInitiators()) { initiatorURIs.add(URI.create(initiatorURIStr)); } // Invoke the export group create operation, // which should in turn create a workflow operations to // create the export for the newly added volume(s). exportGroupCreate(storageURI, exportGroupURI, initiatorURIs, volumeMap, token); anyVolumesAdded = true; } else { _log.warn("There are no initiator for export group: " + exportGroup.getLabel()); } } if (!anyVolumesAdded && !createdNewMask) { taskCompleter.ready(_dbClient); _log.info( "No volumes pushed to array because either they already exist " + "or there were no initiators added to the export yet."); } } catch (Exception ex) { _log.error("ExportGroup Orchestration failed.", ex); // TODO add service code here if (taskCompleter != null) { ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(ex.getMessage(), ex); taskCompleter.error(_dbClient, serviceError); } } }