use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.
the class VPlexBackendManager method verifyExportMaskOnSystem.
/**
* Verify that an ExportMask that is going to be used is on the StorageSystem.
*
* @param mask
* @param array
*/
private void verifyExportMaskOnSystem(ExportMask mask, StorageSystem array) {
VplexBackEndMaskingOrchestrator maskingOrchestrator = getOrch(array);
BlockStorageDevice storageDevice = _blockDeviceController.getDevice(array.getSystemType());
// Make a list of Initiators used by the ExportMask. These could be initiators
// explicitly named in the Export Mask, or Initiators that match addresses in the existingInitiators
// fields. (The later occurs for externally created ExportMasks.)
List<Initiator> initiators = new ArrayList<Initiator>();
initiators.addAll(ExportMaskUtils.getInitiatorsForExportMask(_dbClient, mask, Transport.FC));
if (initiators.isEmpty()) {
initiators.addAll(ExportMaskUtils.getExistingInitiatorsForExportMask(_dbClient, mask, Transport.FC));
}
Map<URI, ExportMask> maskSet = maskingOrchestrator.readExistingExportMasks(array, storageDevice, initiators);
if (maskSet.containsKey(mask.getId())) {
_log.info(String.format("Verified ExportMask %s present on %s", mask.getMaskName(), array.getNativeGuid()));
return;
}
}
use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.
the class VPlexBackendManager method addWorkflowStepsToRemoveBackendVolumes.
/**
* Remove a list of volumes from the ExportGroup specified.
*
* @param workflow
* = Workflow steps are to be added to
* @param waitFor
* - Wait for completion of this workflow step
* @param storage
* - Storage SclusterUnknownystem
* @param exportGroupURI-
* Export Group to be processed
* @param blockObjectList
* - list of volumes or snapshot (URIs)
* @return true if any steps added to Workflow
* @throws DeviceControllerException
*/
public boolean addWorkflowStepsToRemoveBackendVolumes(Workflow workflow, String waitFor, StorageSystem storage, URI exportGroupURI, List<URI> blockObjectList) throws DeviceControllerException {
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
boolean stepsAdded = false;
// Read all the ExportMasks
Map<String, ExportMask> exportMasks = new HashMap<String, ExportMask>();
Map<String, List<URI>> maskToVolumes = new HashMap<String, List<URI>>();
List<ExportMask> egExportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup);
for (ExportMask mask : egExportMasks) {
if (mask == null || mask.getInactive()) {
continue;
}
exportMasks.put(mask.getId().toString(), mask);
maskToVolumes.put(mask.getId().toString(), new ArrayList<URI>());
}
// Put this information in the maskToVolumes map.
for (URI blockObjectURI : blockObjectList) {
for (ExportMask mask : exportMasks.values()) {
if (mask.hasVolume(blockObjectURI)) {
maskToVolumes.get(mask.getId().toString()).add(blockObjectURI);
} else {
_log.info(String.format("ExportMask %s (%s) does not contain volume %s", mask.getMaskName(), mask.getId(), blockObjectURI));
}
}
}
// Now process each Export Mask.
// refresh export masks per XIO storage array
boolean needRefresh = storage.deviceIsType(Type.xtremio);
String previousStepId = waitFor;
for (ExportMask mask : exportMasks.values()) {
List<URI> volumes = maskToVolumes.get(mask.getId().toString());
if (volumes.isEmpty()) {
_log.info("No volumes to remove for Export Mask: " + mask.getId());
continue;
}
previousStepId = waitFor;
// Verify the ExportMask is present on the system, or check if it was renamed
verifyExportMaskOnSystem(mask, storage);
if (mask.getCreatedBySystem()) {
_log.info(String.format("Generating unzoning step for ExportMask %s", mask.getMaskName()));
// Since this mask was created by the system, we want to unzone it.
List<URI> maskURIs = Collections.singletonList(mask.getId());
List<NetworkZoningParam> zoningParams = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), maskURIs, _dbClient);
Workflow.Method zoneRemoveMethod = _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParams, volumes);
previousStepId = workflow.createStep(ZONING_STEP, String.format("Removing zones for ExportMask %s", mask.getMaskName()), previousStepId, nullURI, "network-system", _networkDeviceController.getClass(), zoneRemoveMethod, zoneRemoveMethod, null);
} else {
_log.info(String.format("ExportMask %s not created by ViPR; no unzoning step", mask.getMaskName()));
}
VplexBackEndMaskingOrchestrator orca = getOrch(storage);
List<URI> initiatorURIs = new ArrayList<>();
if (mask.getInitiators() != null) {
initiatorURIs = new ArrayList<URI>(Collections2.transform(mask.getInitiators(), CommonTransformerFunctions.FCTN_STRING_TO_URI));
}
if (needRefresh) {
BlockStorageDevice device = _blockDeviceController.getDevice(storage.getSystemType());
device.refreshExportMask(storage, mask);
needRefresh = false;
}
Workflow.Method removeVolumesMethod = orca.deleteOrRemoveVolumesFromExportMaskMethod(storage.getId(), exportGroup.getId(), mask.getId(), volumes, initiatorURIs);
String stepId = workflow.createStepId();
workflow.createStep(EXPORT_STEP, String.format("Removing volume from ExportMask %s", mask.getMaskName()), previousStepId, storage.getId(), storage.getSystemType(), orca.getClass(), removeVolumesMethod, removeVolumesMethod, stepId);
_log.info(String.format("Generated remove volume from ExportMask %s for volumes %s", mask.getMaskName(), volumes));
stepsAdded = true;
}
return stepsAdded;
}
use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.
the class VPlexBackendManager method chooseBackendExportMask.
/**
* Choose one of the existing Export Masks (on VMAX: masking views) if possible in
* which to place the volume to be exported to the VPlex. Otherwise ExportMask(s)
* will be generated and one will be chosen from the generated set.
*
* @param vplex
* [IN] - VPlex storage system
* @param array
* [IN] - Storage Array storage system
* @param varrayURI
* [IN] - Virtual array
* @param volumeMap
* [IN] - Map of URI to their corresponding Volume object
* @param stepId
* the workflow step id used find the workflow where the existing zone information is stored
* @return ExportMaskPlacementDescriptor - data structure that will indicate the mapping of ExportMasks to
* ExportGroups and ExportMasks to Volumes.
* @throws ControllerException
*/
public ExportMaskPlacementDescriptor chooseBackendExportMask(StorageSystem vplex, StorageSystem array, URI varrayURI, Map<URI, Volume> volumeMap, String stepId) throws ControllerException {
_log.info(String.format("Searching for existing ExportMasks between Vplex %s (%s) and Array %s (%s) in Varray %s", vplex.getLabel(), vplex.getNativeGuid(), array.getLabel(), array.getNativeGuid(), varrayURI));
long startTime = System.currentTimeMillis();
// The volumeMap can contain volumes from different arrays. We are interested only in the ones for 'array'.
Map<URI, Volume> volumesForArray = filterVolumeMap(volumeMap, array);
// Build the data structures used for analysis and validation.
buildDataStructures(vplex, array, varrayURI);
VplexBackEndMaskingOrchestrator vplexBackendOrchestrator = getOrch(array);
BlockStorageDevice storageDevice = _blockDeviceController.getDevice(array.getSystemType());
// Lock operation.
String lockName = _vplexApiLockManager.getLockName(vplex.getId(), _cluster, array.getId());
boolean lockAcquired = false;
try {
if (_vplexApiLockManager != null) {
lockAcquired = _vplexApiLockManager.acquireLock(lockName, MAX_LOCK_WAIT_SECONDS);
if (!lockAcquired) {
_log.info("Timed out waiting on lock- PROCEEDING ANYWAY!");
}
}
// Initialize the placement data structure
ExportMaskPlacementDescriptor placementDescriptor = ExportMaskPlacementDescriptor.create(_tenantURI, _projectURI, vplex, array, varrayURI, volumesForArray, _idToInitiatorMap.values());
// VplexBackEndMaskingOrchestrator#suggestExportMasksForPlacement should fill in the rest of the
// placement data structures, such that decisions on how to reuse the ExportMasks can be done here.
// At a minimum, this placement is done based on reading the ExportMasks off the backend array based on the
// initiators.
// Customizations can be done per array based on other factors. Most notably, for the case of VMAX, this
// would
// place volumes in appropriate ExportMasks based on the volume's AutoTieringPolicy relationship.
vplexBackendOrchestrator.suggestExportMasksForPlacement(array, storageDevice, _initiators, placementDescriptor);
// Apply the filters that will remove any ExportMasks that do not fit the expected VPlex masking paradigm
Set<URI> invalidMasks = filterExportMasksByVPlexRequirements(vplex, array, varrayURI, placementDescriptor);
// If there were any invalid masks found, we can redo the volume placement into
// an alternative ExportMask (if there are any listed by the descriptor)
putUnplacedVolumesIntoAlternativeMask(placementDescriptor);
// If not, we will attempt to generate some.
if (!placementDescriptor.hasMasks()) {
_log.info("There weren't any ExportMasks in the placementDescriptor. Creating new ExportMasks for the volumes.");
// Did not find any reusable ExportMasks. Either there were some that matched initiators, but did not
// meeting the
// VPlex criteria, or there were no existing masks for the backend at all.
Map<URI, Volume> volumesToPlace = placementDescriptor.getVolumesToPlace();
createVPlexBackendExportMasksForVolumes(vplex, array, varrayURI, placementDescriptor, invalidMasks, volumesToPlace, stepId);
} else if (placementDescriptor.hasUnPlacedVolumes()) {
_log.info("There were some reusable ExportMasks found, but not all volumes got placed. Will create an ExportMask to " + "hold these unplaced volumes.");
// There were some matching ExportMasks found on the backend array, but we also have some
// unplaced
// volumes. We need to create new ExportMasks to hold these unplaced volumes.
// We will leave the placement hint to whatever was determined by the suggestExportMasksForPlacement
// call
Map<URI, Volume> unplacedVolumes = placementDescriptor.getUnplacedVolumes();
createVPlexBackendExportMasksForVolumes(vplex, array, varrayURI, placementDescriptor, invalidMasks, unplacedVolumes, stepId);
}
// At this point, we have:
//
// a). Requested that the backend StorageArray provide us with a list of ExportMasks that can support the
// initiators + volumes.
// b). Processed the suggested ExportMasks in case they had their names changed
// c). Filtered out any ExportMasks that do not fit the VPlex masking paradigm
// OR
// d). Created a set of new ExportMasks to support the initiators + volumes
//
// We will now run the final placement based on a strategy determined by looking at the placementDescriptor
VPlexBackendPlacementStrategyFactory.create(_dbClient, placementDescriptor).execute();
long elapsed = System.currentTimeMillis() - startTime;
_log.info(String.format("PlacementDescriptor processing took %f seconds", (double) elapsed / (double) 1000));
_log.info(String.format("PlacementDescriptor was created:%n%s", placementDescriptor.toString()));
return placementDescriptor;
} finally {
if (lockAcquired) {
_vplexApiLockManager.releaseLock(lockName);
}
}
}
use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.
the class VPlexDeviceController method createWorkflowStepsForBlockVolumeExport.
/**
* Create workflow steps in the passed workflow to create export groups for the
* front end ports of the passed storage systems to the backend ports of the
* passed VPlex storage system for the purpose of exposing volumes on these
* storage system to the VPlex.
*
* @param workflow
* The workflow to which the export steps are added.
* @param vplexSystem
* A reference to the VPlex storage system.
* @param storageSystemMap
* The list of storage systems to export.
* @param volumeMap
* The volumes to be exported.
* @param projectURI
* The project reference.
* @param tenantURI
* The tenant reference.
* @param dependantStepId
* The dependent step if, typically a volume creation step.
*
* @throws IOException
* When an error occurs.
* @throws ControllerException
*/
private String createWorkflowStepsForBlockVolumeExport(Workflow workflow, StorageSystem vplexSystem, Map<URI, StorageSystem> storageSystemMap, Map<URI, Volume> volumeMap, URI projectURI, URI tenantURI, String dependantStepId) throws IOException, ControllerException {
String lastStep = dependantStepId;
// The following adds a rollback step to forget the volumes if something
// goes wrong. This is a nop on provisioning.
lastStep = addRollbackStepToForgetVolumes(workflow, vplexSystem.getId(), new ArrayList<URI>(volumeMap.keySet()), lastStep);
String forgetRBStep = lastStep;
URI vplexURI = vplexSystem.getId();
// Populate the container for the export workflow step generation
for (Map.Entry<URI, StorageSystem> storageEntry : storageSystemMap.entrySet()) {
URI storageSystemURI = storageEntry.getKey();
StorageSystem storageSystem = storageEntry.getValue();
URI varray = getVolumesVarray(storageSystem, volumeMap.values());
_log.info(String.format("Creating ExportGroup for storage system %s (%s) in Virtual Aarray[(%s)]", storageSystem.getLabel(), storageSystemURI, varray));
if (varray == null) {
// For whatever reason, there were no Volumes for this Storage System found, so we
// definitely do not want to create anything. Log a warning and continue.
_log.warn(String.format("No Volumes for storage system %s (%s), no need to create an ExportGroup.", storageSystem.getLabel(), storageSystemURI));
continue;
}
// Get the initiator port map for this VPLEX and storage system
// for this volumes virtual array.
VPlexBackendManager backendMgr = new VPlexBackendManager(_dbClient, this, _blockDeviceController, _blockScheduler, _networkDeviceController, projectURI, tenantURI, _vplexApiLockManager, coordinator);
Map<URI, List<StoragePort>> initiatorPortMap = backendMgr.getInitiatorPortsForArray(vplexURI, storageSystemURI, varray);
// If there are no networks that can be zoned, error.
if (initiatorPortMap.isEmpty()) {
throw DeviceControllerException.exceptions.noNetworksConnectingVPlexToArray(vplexSystem.getNativeGuid(), storageSystem.getNativeGuid());
}
// Select from an existing ExportMask if possible
ExportMaskPlacementDescriptor descriptor = backendMgr.chooseBackendExportMask(vplexSystem, storageSystem, varray, volumeMap, lastStep);
// refresh export masks per XIO storage array
boolean needRefresh = storageSystem.deviceIsType(DiscoveredDataObject.Type.xtremio);
// For every ExportMask in the descriptor ...
for (URI exportMaskURI : descriptor.getPlacedMasks()) {
// Create steps to place each set of volumes into its assigned ExportMask
ExportGroup exportGroup = descriptor.getExportGroupForMask(exportMaskURI);
ExportMask exportMask = descriptor.getExportMask(exportMaskURI);
Map<URI, Volume> placedVolumes = descriptor.getPlacedVolumes(exportMaskURI);
if (needRefresh) {
BlockStorageDevice device = _blockDeviceController.getDevice(storageSystem.getSystemType());
device.refreshExportMask(storageSystem, exportMask);
needRefresh = false;
;
}
// Add the workflow steps.
lastStep = backendMgr.addWorkflowStepsToAddBackendVolumes(workflow, lastStep, exportGroup, exportMask, placedVolumes, varray, vplexSystem, storageSystem, forgetRBStep);
}
}
return lastStep;
}
use of com.emc.storageos.volumecontroller.BlockStorageDevice in project coprhd-controller by CoprHD.
the class VNXeMaskingOrchestrator method exportGroupCreate.
/**
* Create storage level masking components to support the requested
* ExportGroup object. ExportMask will be created for each host.
*
* @param storageURI
* - URI referencing underlying storage array
* @param exportGroupURI
* - URI referencing Bourne-level masking, ExportGroup
* @param initiatorURIs
* - List of Initiator URIs
* @param volumeMap
* - Map of Volume URIs to requested Integer URI
* @param token
* - Identifier for operation
* @throws Exception
*/
@Override
public void exportGroupCreate(URI storageURI, URI exportGroupURI, List<URI> initiatorURIs, Map<URI, Integer> volumeMap, String token) throws Exception {
ExportOrchestrationTask taskCompleter = null;
try {
BlockStorageDevice device = getDevice();
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
if (initiatorURIs != null && !initiatorURIs.isEmpty()) {
_log.info("export_create: initiator list non-empty");
// Set up workflow steps.
Workflow workflow = _workflowService.getNewWorkflow(MaskingWorkflowEntryPoints.getInstance(), "exportGroupCreate", true, token);
// Create two steps, one for Zoning, one for the ExportGroup actions.
// This step is for zoning. It is not specific to a single
// NetworkSystem, as it will look at all the initiators and targets and compute
// the zones required (which might be on multiple NetworkSystems.)
boolean createdSteps = determineExportGroupCreateSteps(workflow, null, device, storage, exportGroup, initiatorURIs, volumeMap, token);
String zoningStep = generateZoningCreateWorkflow(workflow, EXPORT_GROUP_MASKING_TASK, exportGroup, null, volumeMap);
if (createdSteps) {
// Execute the plan and allow the WorkflowExecutor to fire the
// taskCompleter.
String successMessage = String.format("ExportGroup successfully applied for StorageArray %s", storage.getLabel());
workflow.executePlan(taskCompleter, successMessage);
}
} else {
_log.info("export_create: initiator list");
taskCompleter.ready(_dbClient);
}
} catch (Exception ex) {
_log.error("ExportGroup Orchestration failed.", ex);
// TODO add service code here
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(ex.getMessage(), ex);
taskCompleter.error(_dbClient, serviceError);
}
}
}
Aggregations