use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskAddVolumeCompleter in project coprhd-controller by CoprHD.
the class AbstractDefaultMaskingOrchestrator method generateExportMaskAddVolumesWorkflow.
/**
* Generate workflow steps to add volumes to an export mask
*
* @param workflow
* workflow
* @param previousStep
* previous step ID
* @param storage
* storage system
* @param exportGroup
* export group
* @param exportMask
* export mask
* @param volumesToAdd
* volumes to add to the mask
* @param initiators
* initiators that should be impacted by this operation (and for rollback)
* @return step ID
* @throws Exception
*/
public String generateExportMaskAddVolumesWorkflow(Workflow workflow, String previousStep, StorageSystem storage, ExportGroup exportGroup, ExportMask exportMask, Map<URI, Integer> volumesToAdd, List<URI> initiators) throws Exception {
URI exportGroupURI = exportGroup.getId();
URI exportMaskURI = exportMask.getId();
URI storageURI = storage.getId();
String maskingStep = workflow.createStepId();
ExportTaskCompleter exportTaskCompleter = new ExportMaskAddVolumeCompleter(exportGroupURI, exportMask.getId(), volumesToAdd, maskingStep);
Workflow.Method maskingExecuteMethod = new Workflow.Method("doExportGroupAddVolumes", storageURI, exportGroupURI, exportMaskURI, volumesToAdd, initiators, exportTaskCompleter);
Workflow.Method maskingRollbackMethod = new Workflow.Method("rollbackExportGroupAddVolumes", storageURI, exportGroupURI, exportMaskURI, volumesToAdd, initiators, maskingStep);
maskingStep = workflow.createStep(EXPORT_GROUP_MASKING_TASK, String.format("Adding volumes to mask %s (%s)", exportMask.getMaskName(), exportMask.getId().toString()), previousStep, storageURI, storage.getSystemType(), MaskingWorkflowEntryPoints.class, maskingExecuteMethod, maskingRollbackMethod, maskingStep);
return maskingStep;
}
use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskAddVolumeCompleter in project coprhd-controller by CoprHD.
the class VmaxMaskingOrchestrator method changePortGroup.
@Override
public void changePortGroup(URI storageURI, URI exportGroupURI, URI portGroupURI, List<URI> exportMaskURIs, boolean waitForApproval, String token) {
ExportChangePortGroupCompleter taskCompleter = null;
try {
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
StoragePortGroup portGroup = _dbClient.queryObject(StoragePortGroup.class, portGroupURI);
taskCompleter = new ExportChangePortGroupCompleter(storageURI, exportGroupURI, token, portGroupURI);
logExportGroup(exportGroup, storageURI);
String workflowKey = "changePortGroup";
if (_workflowService.hasWorkflowBeenCreated(token, workflowKey)) {
return;
}
Workflow workflow = _workflowService.getNewWorkflow(MaskingWorkflowEntryPoints.getInstance(), workflowKey, false, token);
if (CollectionUtils.isEmpty(exportMaskURIs)) {
_log.info("No export masks to change");
taskCompleter.ready(_dbClient);
return;
}
List<ExportMask> exportMasks = _dbClient.queryObject(ExportMask.class, exportMaskURIs);
String previousStep = null;
Set<URI> hostURIs = new HashSet<URI>();
for (ExportMask oldMask : exportMasks) {
// create a new masking view using the new port group
SmisStorageDevice device = (SmisStorageDevice) getDevice();
oldMask = device.refreshExportMask(storage, oldMask);
StringSet existingInits = oldMask.getExistingInitiators();
StringMap existingVols = oldMask.getExistingVolumes();
if (!CollectionUtils.isEmpty(existingInits)) {
String error = String.format("The export mask %s has unmanaged initiators %s", oldMask.getMaskName(), Joiner.on(',').join(existingInits));
_log.error(error);
ServiceError serviceError = DeviceControllerException.errors.changePortGroupValidationError(error);
taskCompleter.error(_dbClient, serviceError);
return;
}
if (!CollectionUtils.isEmpty(existingVols)) {
String error = String.format("The export mask %s has unmanaged volumes %s", oldMask.getMaskName(), Joiner.on(',').join(existingVols.keySet()));
_log.error(error);
ServiceError serviceError = DeviceControllerException.errors.changePortGroupValidationError(error);
taskCompleter.error(_dbClient, serviceError);
return;
}
InitiatorHelper initiatorHelper = new InitiatorHelper(StringSetUtil.stringSetToUriList(oldMask.getInitiators())).process(exportGroup);
List<String> initiatorNames = initiatorHelper.getPortNames();
List<URI> volumes = StringSetUtil.stringSetToUriList(oldMask.getVolumes().keySet());
ExportPathParams pathParams = _blockScheduler.calculateExportPathParamForVolumes(volumes, 0, storageURI, exportGroupURI);
pathParams.setStoragePorts(portGroup.getStoragePorts());
List<Initiator> initiators = ExportUtils.getExportMaskInitiators(oldMask, _dbClient);
List<URI> initURIs = new ArrayList<URI>();
for (Initiator init : initiators) {
if (!NullColumnValueGetter.isNullURI(init.getHost())) {
hostURIs.add(init.getHost());
}
initURIs.add(init.getId());
}
// Get impacted export groups
List<ExportGroup> impactedExportGroups = ExportMaskUtils.getExportGroups(_dbClient, oldMask);
List<URI> exportGroupURIs = URIUtil.toUris(impactedExportGroups);
_log.info("changePortGroup: exportMask {}, impacted export groups: {}", oldMask.getMaskName(), Joiner.on(',').join(exportGroupURIs));
Map<URI, List<URI>> assignments = _blockScheduler.assignStoragePorts(storage, exportGroup, initiators, null, pathParams, volumes, _networkDeviceController, exportGroup.getVirtualArray(), token);
// Trying to find if there is existing export mask or masking view for the same host and using the new
// port group. If found one, add the volumes in the current export mask to the new one; otherwise, create
// a new export mask/masking view, with the same storage group, initiator group and the new port group.
// then delete the current export mask.
ExportMask newMask = device.findExportMasksForPortGroupChange(storage, initiatorNames, portGroupURI);
Map<URI, Integer> volumesToAdd = StringMapUtil.stringMapToVolumeMap(oldMask.getVolumes());
if (newMask != null) {
updateZoningMap(exportGroup, newMask, true);
_log.info(String.format("adding these volumes %s to mask %s", Joiner.on(",").join(volumesToAdd.keySet()), newMask.getMaskName()));
previousStep = generateZoningAddVolumesWorkflow(workflow, previousStep, exportGroup, Arrays.asList(newMask), new ArrayList<URI>(volumesToAdd.keySet()));
String addVolumeStep = workflow.createStepId();
ExportTaskCompleter exportTaskCompleter = new ExportMaskAddVolumeCompleter(exportGroupURI, newMask.getId(), volumesToAdd, addVolumeStep);
exportTaskCompleter.setExportGroups(exportGroupURIs);
Workflow.Method maskingExecuteMethod = new Workflow.Method("doExportGroupAddVolumes", storageURI, exportGroupURI, newMask.getId(), volumesToAdd, null, exportTaskCompleter);
Workflow.Method maskingRollbackMethod = new Workflow.Method("rollbackExportGroupAddVolumes", storageURI, exportGroupURI, exportGroupURIs, newMask.getId(), volumesToAdd, initURIs, addVolumeStep);
previousStep = workflow.createStep(EXPORT_GROUP_MASKING_TASK, String.format("Adding volumes to mask %s (%s)", newMask.getMaskName(), newMask.getId().toString()), previousStep, storageURI, storage.getSystemType(), MaskingWorkflowEntryPoints.class, maskingExecuteMethod, maskingRollbackMethod, addVolumeStep);
previousStep = generateExportMaskAddVolumesWorkflow(workflow, previousStep, storage, exportGroup, newMask, volumesToAdd, null);
} else {
// We don't find existing export mask /masking view, we will create a new one.
// first, to construct the new export mask name, if the export mask has the original name, then
// append the new port group name to the current export mask name; if the export mask already has the current
// port group name appended, then remove the current port group name, and append the new one.
String oldName = oldMask.getMaskName();
URI oldPGURI = oldMask.getPortGroup();
if (oldPGURI != null) {
StoragePortGroup oldPG = _dbClient.queryObject(StoragePortGroup.class, oldPGURI);
if (oldPG != null) {
String pgName = oldPG.getLabel();
if (oldName.endsWith(pgName)) {
oldName = oldName.replaceAll(pgName, "");
}
}
}
String maskName = null;
if (oldName.endsWith("_")) {
maskName = String.format("%s%s", oldName, portGroup.getLabel());
} else {
maskName = String.format("%s_%s", oldName, portGroup.getLabel());
}
newMask = ExportMaskUtils.initializeExportMask(storage, exportGroup, initiators, volumesToAdd, getStoragePortsInPaths(assignments), assignments, maskName, _dbClient);
newMask.setPortGroup(portGroupURI);
List<BlockObject> vols = new ArrayList<BlockObject>();
for (URI boURI : volumesToAdd.keySet()) {
BlockObject bo = BlockObject.fetch(_dbClient, boURI);
vols.add(bo);
}
newMask.addToUserCreatedVolumes(vols);
_dbClient.updateObject(newMask);
_log.info(String.format("Creating new exportMask %s", maskName));
// Make a new TaskCompleter for the exportStep. It has only one subtask.
// This is due to existing requirements in the doExportGroupCreate completion
// logic.
String maskingStep = workflow.createStepId();
ExportTaskCompleter exportTaskCompleter = new ExportMaskChangePortGroupAddMaskCompleter(newMask.getId(), exportGroupURI, maskingStep);
exportTaskCompleter.setExportGroups(exportGroupURIs);
Workflow.Method maskingExecuteMethod = new Workflow.Method("doExportChangePortGroupAddPaths", storageURI, exportGroupURI, newMask.getId(), oldMask.getId(), portGroupURI, exportTaskCompleter);
Workflow.Method maskingRollbackMethod = new Workflow.Method("rollbackExportGroupCreate", storageURI, exportGroupURI, newMask.getId(), maskingStep);
maskingStep = workflow.createStep(EXPORT_GROUP_MASKING_TASK, String.format("Create export mask(%s) to use port group %s", newMask.getMaskName(), portGroup.getNativeGuid()), previousStep, storageURI, storage.getSystemType(), MaskingWorkflowEntryPoints.class, maskingExecuteMethod, maskingRollbackMethod, maskingStep);
String zoningStep = workflow.createStepId();
List<URI> masks = new ArrayList<URI>();
masks.add(newMask.getId());
previousStep = generateZoningCreateWorkflow(workflow, maskingStep, exportGroup, masks, volumesToAdd, zoningStep);
}
}
previousStep = _wfUtils.generateHostRescanWorkflowSteps(workflow, hostURIs, previousStep);
if (waitForApproval) {
// Insert a step that will be suspended. When it resumes, it will re-acquire the lock keys,
// which are released when the workflow suspends.
List<String> lockKeys = ControllerLockingUtil.getHostStorageLockKeys(_dbClient, ExportGroup.ExportGroupType.valueOf(exportGroup.getType()), StringSetUtil.stringSetToUriList(exportGroup.getInitiators()), storageURI);
String suspendMessage = "Adjust/rescan host/cluster paths. Press \"Resume\" to start removal of unnecessary paths." + "\"Rollback\" will terminate the order and roll back";
Workflow.Method method = WorkflowService.acquireWorkflowLocksMethod(lockKeys, LockTimeoutValue.get(LockType.EXPORT_GROUP_OPS));
Workflow.Method rollbackNull = Workflow.NULL_METHOD;
previousStep = workflow.createStep("AcquireLocks", "Suspending for user verification of host/cluster connectivity.", previousStep, storage.getId(), storage.getSystemType(), WorkflowService.class, method, rollbackNull, waitForApproval, null);
workflow.setSuspendedStepMessage(previousStep, suspendMessage);
}
for (ExportMask exportMask : exportMasks) {
previousStep = generateChangePortGroupDeleteMaskWorkflowstep(storageURI, exportGroup, exportMask, previousStep, workflow);
}
_wfUtils.generateHostRescanWorkflowSteps(workflow, hostURIs, previousStep);
if (!workflow.getAllStepStatus().isEmpty()) {
_log.info("The change port group workflow has {} steps. Starting the workflow.", workflow.getAllStepStatus().size());
// update ExportChangePortGroupCompleter with affected export groups
Set<URI> affectedExportGroups = new HashSet<URI>();
for (ExportMask mask : exportMasks) {
List<ExportGroup> assocExportGroups = ExportMaskUtils.getExportGroups(_dbClient, mask);
for (ExportGroup eg : assocExportGroups) {
affectedExportGroups.add(eg.getId());
}
}
taskCompleter.setAffectedExportGroups(affectedExportGroups);
workflow.executePlan(taskCompleter, "Change port group successfully.");
_workflowService.markWorkflowBeenCreated(token, workflowKey);
} else {
taskCompleter.ready(_dbClient);
}
} catch (Exception e) {
_log.error("Export change port group Orchestration failed.", e);
if (taskCompleter != null) {
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(e.getMessage(), e);
taskCompleter.error(_dbClient, serviceError);
}
}
}
use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskAddVolumeCompleter in project coprhd-controller by CoprHD.
the class VPlexBackendManager method addWorkflowStepsToAddBackendVolumes.
/**
* Add steps to generate the Workflow to add a volume to the VPLEX backend.
* The VNX is special, we do zoning after masking.
* For all the other arrays, we do zoning, then masking.
*
* @param workflow
* @param dependantStepId
* @param exportGroup
* @param exportMask
* @param volumeMap
* @param varrayURI
* @param vplex
* @param array
* @param forgetRollbackStepId
* @return String stepId of last added step
*/
public String addWorkflowStepsToAddBackendVolumes(Workflow workflow, String dependantStepId, ExportGroup exportGroup, ExportMask exportMask, Map<URI, Volume> volumeMap, URI varrayURI, StorageSystem vplex, StorageSystem array, String forgetRollbackStepId) {
// Determine if VNX or OpenStack so can order VNX zoning after masking
boolean isMaskingFirst = isMaskingFirst(array);
boolean isOpenStack = isOpenStack(array);
Map<URI, Integer> volumeLunIdMap = createVolumeMap(array.getId(), volumeMap);
String zoningStep = null;
String maskStepId = workflow.createStepId();
String reValidateExportMaskStep = workflow.createStepId();
ExportMaskAddVolumeCompleter createCompleter = new ExportMaskAddVolumeCompleter(exportGroup.getId(), exportMask.getId(), volumeLunIdMap, maskStepId, forgetRollbackStepId);
List<URI> volumeList = new ArrayList<>();
volumeList.addAll(volumeLunIdMap.keySet());
String previousStepId = dependantStepId;
String zoningDependentStep = ((isMaskingFirst && isOpenStack) ? reValidateExportMaskStep : ((isMaskingFirst && !isOpenStack) ? maskStepId : previousStepId));
if (exportMask.getCreatedBySystem()) {
_log.info(String.format("Creating zone references for Backend ExportMask %s", exportMask.getMaskName()));
List<URI> maskURIs = Collections.singletonList(exportMask.getId());
List<NetworkZoningParam> zoningParams = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), maskURIs, _dbClient);
HashSet<URI> volumes = new HashSet<URI>(volumeLunIdMap.keySet());
Workflow.Method zoneCreateMethod = _networkDeviceController.zoneExportAddVolumesMethod(exportGroup.getId(), maskURIs, volumes);
Workflow.Method zoneDeleteMethod = _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParams, volumes);
zoningStep = workflow.createStep(ZONING_STEP, String.format("Adding zones for ExportMask %s", exportMask.getMaskName()), zoningDependentStep, nullURI, "network-system", _networkDeviceController.getClass(), zoneCreateMethod, zoneDeleteMethod, null);
if (!isMaskingFirst) {
previousStepId = zoningStep;
}
}
// Initiators that are sent down for export validation are the known initiators.
// For back-end VPLEX masks, I find that userAddedInitiators are not getting filled-in,
// so we're playing it safe by using known initiators.
List<URI> initiatorURIs = new ArrayList<>();
if (exportMask.getInitiators() != null) {
initiatorURIs = new ArrayList<URI>(Collections2.transform(exportMask.getInitiators(), CommonTransformerFunctions.FCTN_STRING_TO_URI));
}
VplexBackEndMaskingOrchestrator orca = getOrch(array);
Workflow.Method updateMaskMethod = orca.createOrAddVolumesToExportMaskMethod(array.getId(), exportGroup.getId(), exportMask.getId(), volumeLunIdMap, initiatorURIs, createCompleter);
Workflow.Method rollbackMaskMethod = orca.deleteOrRemoveVolumesFromExportMaskMethod(array.getId(), exportGroup.getId(), exportMask.getId(), volumeList, initiatorURIs);
workflow.createStep(EXPORT_STEP, "createOrAddVolumesToExportMask: " + exportMask.getMaskName(), previousStepId, array.getId(), array.getSystemType(), orca.getClass(), updateMaskMethod, rollbackMaskMethod, maskStepId);
// This is required as the export mask gets updated by reading the cinder response.
if (isOpenStack) {
// START - updateZoningMapAndValidateExportMask Step
Workflow.Method updatezoningAndvalidateMaskMethod = ((VplexCinderMaskingOrchestrator) orca).updateZoningMapAndValidateExportMaskMethod(varrayURI, _initiatorPortMap, exportMask.getId(), _directorToInitiatorIds, _idToInitiatorMap, _portWwnToClusterMap, vplex, array, _cluster);
workflow.createStep(REVALIDATE_MASK, "updatezoningAndrevalidateExportMask: " + exportMask.getMaskName(), maskStepId, array.getId(), array.getSystemType(), orca.getClass(), updatezoningAndvalidateMaskMethod, rollbackMaskMethod, reValidateExportMaskStep);
// END - updateZoningMapAndValidateExportMask Step
}
_log.info(String.format("VPLEX ExportGroup %s (%s) vplex %s varray %s", exportGroup.getLabel(), exportGroup.getId(), vplex.getId(), exportGroup.getVirtualArray()));
return (isMaskingFirst && zoningStep != null) ? zoningStep : maskStepId;
}
use of com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskAddVolumeCompleter in project coprhd-controller by CoprHD.
the class VPlexDeviceController method storageViewAddVolumes.
/**
* Method for adding volumes to a single ExportMask.
*
* @param vplexURI
* @param exportGroupURI
* @param exportMaskURI
* @param volumeMap
* @param opId
* @throws ControllerException
*/
public void storageViewAddVolumes(URI vplexURI, URI exportGroupURI, URI exportMaskURI, Map<URI, Integer> volumeMap, String opId) throws ControllerException {
String volListStr = "";
ExportMaskAddVolumeCompleter completer = null;
try {
WorkflowStepCompleter.stepExecuting(opId);
completer = new ExportMaskAddVolumeCompleter(exportGroupURI, exportMaskURI, volumeMap, opId);
ExportOperationContext context = new VplexExportOperationContext();
// Prime the context object
completer.updateWorkflowStepContext(context);
volListStr = Joiner.on(',').join(volumeMap.keySet());
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
ExportMask exportMask = getDataObject(ExportMask.class, exportMaskURI, _dbClient);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_001);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
// Need to massage the map to fit the API
List<BlockObject> volumes = new ArrayList<BlockObject>();
Map<String, Integer> deviceLabelToHLU = new HashMap<String, Integer>();
boolean duplicateHLU = false;
List<URI> volumesToAdd = new ArrayList<URI>();
String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName), _networkDeviceController);
exportMask = getDataObject(ExportMask.class, exportMaskURI, _dbClient);
for (Map.Entry<URI, Integer> entry : volumeMap.entrySet()) {
if (exportMask.hasVolume(entry.getKey())) {
_log.info(String.format("Volume %s is already in Exportmask %s %s hence skipping adding volume again. This must be shared exportmask. ", entry.getKey(), exportMask.getMaskName(), exportMask.getId()));
continue;
}
Integer requestedHLU = entry.getValue();
// If user have provided specific HLU for volume, then check if its already in use
if (requestedHLU.intValue() != VPlexApiConstants.LUN_UNASSIGNED && exportMask.anyVolumeHasHLU(requestedHLU.toString())) {
String message = String.format("Failed to add Volumes %s to ExportMask %s", volListStr, exportMaskURI);
_log.error(message);
String opName = ResourceOperationTypeEnum.ADD_EXPORT_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.exportHasExistingVolumeWithRequestedHLU(entry.getKey().toString(), requestedHLU.toString(), opName);
failStep(completer, opId, serviceError);
duplicateHLU = true;
break;
}
BlockObject vol = Volume.fetchExportMaskBlockObject(_dbClient, entry.getKey());
volumes.add(vol);
deviceLabelToHLU.put(vol.getDeviceLabel(), requestedHLU);
volumesToAdd.add(entry.getKey());
}
// If duplicate HLU are found then return, completer is set to error above
if (duplicateHLU) {
return;
}
// If deviceLabelToHLU map is empty then volumes already exists in the storage view hence return.
if (deviceLabelToHLU.isEmpty()) {
completer.ready(_dbClient);
return;
}
VPlexStorageViewInfo svInfo = client.addVirtualVolumesToStorageView(exportMask.getMaskName(), vplexClusterName, deviceLabelToHLU);
ExportOperationContext.insertContextOperation(completer, VplexExportOperationContext.OPERATION_ADD_VOLUMES_TO_STORAGE_VIEW, volumesToAdd);
// When VPLEX volumes are exported to a storage view, they get a WWN,
// so set the WWN from the returned storage view information.
Map<URI, Integer> updatedVolumeMap = new HashMap<URI, Integer>();
for (BlockObject volume : volumes) {
String deviceLabel = volume.getDeviceLabel();
String wwn = svInfo.getWWNForStorageViewVolume(volume.getDeviceLabel());
volume.setWWN(wwn);
_dbClient.updateObject(volume);
updatedVolumeMap.put(volume.getId(), svInfo.getHLUForStorageViewVolume(deviceLabel));
// user.
if (exportMask.hasExistingVolume(wwn)) {
_log.info("wwn {} has been added to the storage view {} by the user, but it " + "was already in existing volumes, removing from existing volumes.", wwn, exportMask.forDisplay());
exportMask.removeFromExistingVolumes(wwn);
}
exportMask.addToUserCreatedVolumes(volume);
}
// 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());
exportMask.addVolumes(updatedVolumeMap);
_dbClient.updateObject(exportMask);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_002);
completer.ready(_dbClient);
} catch (VPlexApiException vae) {
String message = String.format("Failed to add Volumes %s to ExportMask %s", volListStr, exportMaskURI);
_log.error(message, vae);
failStep(completer, opId, vae);
} catch (Exception ex) {
String message = String.format("Failed to add Volumes %s to ExportMask %s", volListStr, exportMaskURI);
_log.error(message, ex);
String opName = ResourceOperationTypeEnum.ADD_EXPORT_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.exportGroupAddVolumesFailed(volListStr, exportGroupURI.toString(), opName, ex);
failStep(completer, opId, serviceError);
}
}
Aggregations