use of com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext in project coprhd-controller by CoprHD.
the class VNXeExportOperations method addVolumes.
@Override
public void addVolumes(StorageSystem storage, URI exportMaskUri, VolumeURIHLU[] volumeURIHLUs, List<Initiator> initiatorList, TaskCompleter taskCompleter) throws DeviceControllerException {
_logger.info("{} addVolume START...", storage.getSerialNumber());
List<URI> mappedVolumes = new ArrayList<URI>();
ExportMask exportMask = null;
try {
_logger.info("addVolumes: Export mask id: {}", exportMaskUri);
_logger.info("addVolumes: volume-HLU pairs: {}", Joiner.on(',').join(volumeURIHLUs));
if (initiatorList != null) {
_logger.info("addVolumes: initiators impacted: {}", Joiner.on(',').join(initiatorList));
}
ExportOperationContext context = new VNXeExportOperationContext();
taskCompleter.updateWorkflowStepContext(context);
VNXeApiClient apiClient = getVnxeClient(storage);
exportMask = _dbClient.queryObject(ExportMask.class, exportMaskUri);
if (exportMask == null || exportMask.getInactive()) {
throw new DeviceControllerException("Invalid ExportMask URI: " + exportMaskUri);
}
List<Initiator> initiators = ExportUtils.getExportMaskInitiators(exportMask, _dbClient);
Collection<VNXeHostInitiator> vnxeInitiators = prepareInitiators(initiators).values();
VNXeBase host = apiClient.prepareHostsForExport(vnxeInitiators);
String opId = taskCompleter.getOpId();
Set<String> processedCGs = new HashSet<String>();
for (VolumeURIHLU volURIHLU : volumeURIHLUs) {
URI volUri = volURIHLU.getVolumeURI();
String hlu = volURIHLU.getHLU();
_logger.info(String.format("hlu %s", hlu));
BlockObject blockObject = BlockObject.fetch(_dbClient, volUri);
VNXeExportResult result = null;
Integer newhlu = -1;
if (hlu != null && !hlu.isEmpty() && !hlu.equals(ExportGroup.LUN_UNASSIGNED_STR)) {
newhlu = Integer.valueOf(hlu);
}
// COP-25254 this method could be called when create vplex volumes from snapshot. in this case
// the volume passed in is an internal volume, representing the snapshot. we need to find the snapshot
// with the same nativeGUID, then export the snapshot.
BlockObject snapshot = findSnapshotByInternalVolume(blockObject);
boolean isVplexVolumeFromSnap = false;
URI vplexBackendVol = null;
if (snapshot != null) {
blockObject = snapshot;
exportMask.addVolume(volUri, newhlu);
isVplexVolumeFromSnap = true;
vplexBackendVol = volUri;
volUri = blockObject.getId();
}
String cgName = VNXeUtils.getBlockObjectCGName(blockObject, _dbClient);
if (cgName != null && !processedCGs.contains(cgName)) {
processedCGs.add(cgName);
VNXeUtils.getCGLock(workflowService, storage, cgName, opId);
}
String nativeId = blockObject.getNativeId();
if (URIUtil.isType(volUri, Volume.class)) {
result = apiClient.exportLun(host, nativeId, newhlu);
exportMask.addVolume(volUri, result.getHlu());
if (result.isNewAccess()) {
mappedVolumes.add(volUri);
}
} else if (URIUtil.isType(volUri, BlockSnapshot.class)) {
result = apiClient.exportSnap(host, nativeId, newhlu);
exportMask.addVolume(volUri, result.getHlu());
if (result.isNewAccess()) {
mappedVolumes.add(volUri);
}
String snapWWN = setSnapWWN(apiClient, blockObject, nativeId);
if (isVplexVolumeFromSnap) {
Volume backendVol = _dbClient.queryObject(Volume.class, vplexBackendVol);
backendVol.setWWN(snapWWN);
_dbClient.updateObject(backendVol);
}
}
}
ExportOperationContext.insertContextOperation(taskCompleter, VNXeExportOperationContext.OPERATION_ADD_VOLUMES_TO_HOST_EXPORT, mappedVolumes);
_dbClient.updateObject(exportMask);
// Test mechanism to invoke a failure. No-op on production systems.
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_002);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_logger.error("Add volumes error: ", e);
ServiceError error = DeviceControllerErrors.vnxe.jobFailed("addVolume", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_logger.info("{} addVolumes END...", storage.getSerialNumber());
}
use of com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext in project coprhd-controller by CoprHD.
the class VNXeExportOperations method removeVolumes.
@Override
public void removeVolumes(StorageSystem storage, URI exportMaskUri, List<URI> volumes, List<Initiator> initiatorList, TaskCompleter taskCompleter) throws DeviceControllerException {
_logger.info("{} removeVolumes: START...", storage.getSerialNumber());
try {
_logger.info("removeVolumes: Export mask id: {}", exportMaskUri);
_logger.info("removeVolumes: volumes: {}", Joiner.on(',').join(volumes));
if (initiatorList != null) {
_logger.info("removeVolumes: impacted initiators: {}", Joiner.on(",").join(initiatorList));
}
boolean isRollback = WorkflowService.getInstance().isStepInRollbackState(taskCompleter.getOpId());
if (isRollback) {
List<URI> addedVolumes = new ArrayList<URI>();
// Get the context from the task completer, in case this is a rollback.
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(taskCompleter.getOpId());
if (context != null && context.getOperations() != null) {
_logger.info("Handling removeVolumes as a result of rollback");
ListIterator li = context.getOperations().listIterator(context.getOperations().size());
while (li.hasPrevious()) {
ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
if (operation != null & VNXeExportOperationContext.OPERATION_ADD_VOLUMES_TO_HOST_EXPORT.equals(operation.getOperation())) {
addedVolumes = (List<URI>) operation.getArgs().get(0);
_logger.info("Removing volumes {} as part of rollback", Joiner.on(',').join(addedVolumes));
}
}
}
volumes = addedVolumes;
if (volumes == null || volumes.isEmpty()) {
_logger.info("There was no context found for add volumes. So there is nothing to rollback.");
taskCompleter.ready(_dbClient);
return;
}
}
if (volumes == null || volumes.isEmpty()) {
taskCompleter.ready(_dbClient);
_logger.warn("{} removeVolumes invoked with zero volumes, resulting in no-op....", storage.getSerialNumber());
return;
}
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskUri);
if (exportMask == null || exportMask.getInactive()) {
throw new DeviceControllerException("Invalid ExportMask URI: " + exportMaskUri);
}
List<Initiator> initiators = ExportUtils.getExportMaskInitiators(exportMask, _dbClient);
VNXeApiClient apiClient = getVnxeClient(storage);
String hostId = getHostIdFromInitiators(initiators, apiClient);
if (hostId != null) {
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(storage);
ctx.setExportMask(exportMask);
ctx.setInitiators(initiatorList);
// Allow exceptions to be thrown when not rolling back
ctx.setAllowExceptions(!isRollback);
AbstractVNXeValidator removeVolumesValidator = (AbstractVNXeValidator) validator.removeVolumes(ctx);
removeVolumesValidator.setHostId(hostId);
removeVolumesValidator.validate();
}
String opId = taskCompleter.getOpId();
Set<String> processedCGs = new HashSet<String>();
StringMap volsMap = exportMask.getVolumes();
for (URI volUri : volumes) {
if (hostId != null && volsMap != null && !volsMap.isEmpty() && volsMap.keySet().contains(volUri.toString())) {
BlockObject blockObject = BlockObject.fetch(_dbClient, volUri);
// COP-25254 this method could be called when delete vplex volume created from snapshot. in this case
// the volume passed in is an internal volume, representing the snapshot. we need to find the snapshot
// with the same nativeGUID, then unexport the snapshot.
BlockObject snapshot = findSnapshotByInternalVolume(blockObject);
if (snapshot != null) {
blockObject = snapshot;
exportMask.removeVolume(volUri);
volUri = blockObject.getId();
}
String cgName = VNXeUtils.getBlockObjectCGName(blockObject, _dbClient);
if (cgName != null && !processedCGs.contains(cgName)) {
processedCGs.add(cgName);
VNXeUtils.getCGLock(workflowService, storage, cgName, opId);
}
String nativeId = blockObject.getNativeId();
if (URIUtil.isType(volUri, Volume.class)) {
apiClient.unexportLun(hostId, nativeId);
} else if (URIUtil.isType(volUri, BlockSnapshot.class)) {
apiClient.unexportSnap(hostId, nativeId);
setSnapWWN(apiClient, blockObject, nativeId);
}
}
// update the exportMask object
exportMask.removeVolume(volUri);
}
_dbClient.updateObject(exportMask);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_logger.error("Unexpected error: removeVolumes failed.", e);
ServiceError error = DeviceControllerErrors.vnxe.jobFailed("remove volumes failed", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_logger.info("{} removeVolumes END...", storage.getSerialNumber());
}
use of com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext in project coprhd-controller by CoprHD.
the class VNXeExportOperations method addInitiators.
@Override
public void addInitiators(StorageSystem storage, URI exportMaskUri, List<URI> volumeURIs, List<Initiator> initiatorList, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException {
_logger.info("{} addInitiator START...", storage.getSerialNumber());
List<Initiator> createdInitiators = new ArrayList<Initiator>();
ExportMask exportMask = null;
try {
ExportOperationContext context = new VNXeExportOperationContext();
taskCompleter.updateWorkflowStepContext(context);
exportMask = _dbClient.queryObject(ExportMask.class, exportMaskUri);
if (exportMask == null || exportMask.getInactive()) {
throw new DeviceControllerException("Invalid ExportMask URI: " + exportMaskUri);
}
VNXeApiClient apiClient = getVnxeClient(storage);
List<Initiator> initiators = ExportUtils.getExportMaskInitiators(exportMask, _dbClient);
// Finding existing host from the array
Collection<VNXeHostInitiator> vnxeInitiators = prepareInitiators(initiators).values();
String hostId = null;
for (VNXeHostInitiator init : vnxeInitiators) {
VNXeHostInitiator foundInit = apiClient.getInitiatorByWWN(init.getInitiatorId());
if (foundInit != null) {
VNXeBase host = foundInit.getParentHost();
if (host != null) {
hostId = host.getId();
break;
}
}
}
if (hostId == null) {
String msg = String.format("No existing host found in the array for the existing exportMask %s", exportMask.getMaskName());
_logger.error(msg);
ServiceError error = DeviceControllerErrors.vnxe.jobFailed("addiniator", msg);
taskCompleter.error(_dbClient, error);
return;
}
validateInitiators(_dbClient, initiatorList, apiClient, hostId);
Map<Initiator, VNXeHostInitiator> initiatorMap = prepareInitiators(initiatorList);
for (Entry<Initiator, VNXeHostInitiator> entry : initiatorMap.entrySet()) {
VNXeHostInitiator newInit = entry.getValue();
VNXeHostInitiator init = apiClient.getInitiatorByWWN(newInit.getInitiatorId());
// COP-27752 - fresh deleted initiator may not be deleted completely
int retry = 0;
while (retry <= MAX_REMOVE_INITIATOR_RETRIES && init != null && init.getParentHost() == null) {
try {
Thread.sleep(WAIT_FOR_RETRY);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
init = apiClient.getInitiatorByWWN(newInit.getInitiatorId());
}
if (init != null) {
// found it
VNXeBase host = init.getParentHost();
if (host != null && host.getId().equals(hostId)) {
// do nothing. it is already in the array
_logger.info("The initiator exist in the host in the array");
} else if (host == null) {
// initiator without parent host, add parent host
apiClient.setInitiatorHost(init.getId(), hostId);
} else {
String msg = String.format("Initiator %s belongs to %s, but other initiator belongs to %s. Please move initiator to the correct host", init.getInitiatorId(), host.getId(), hostId);
_logger.error(msg);
if (!createdInitiators.isEmpty()) {
for (Initiator initiator : createdInitiators) {
exportMask.getInitiators().add(initiator.getId().toString());
}
_dbClient.updateObject(exportMask);
ExportOperationContext.insertContextOperation(taskCompleter, VNXeExportOperationContext.OPERATION_ADD_INITIATORS_TO_HOST, createdInitiators);
}
ServiceError error = DeviceControllerErrors.vnxe.jobFailed("addiniator", msg);
taskCompleter.error(_dbClient, error);
return;
}
} else {
apiClient.createInitiator(newInit, hostId);
createdInitiators.add(entry.getKey());
}
}
for (Initiator initiator : initiatorList) {
exportMask.getInitiators().add(initiator.getId().toString());
}
_dbClient.updateObject(exportMask);
ExportOperationContext.insertContextOperation(taskCompleter, VNXeExportOperationContext.OPERATION_ADD_INITIATORS_TO_HOST, createdInitiators);
// Test mechanism to invoke a failure. No-op on production systems.
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_003);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_logger.error("Add initiators error: ", e);
ServiceError error = DeviceControllerErrors.vnxe.jobFailed("addInitiator", e.getMessage());
taskCompleter.error(_dbClient, error);
}
}
use of com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext in project coprhd-controller by CoprHD.
the class VmaxExportOperations method removeInitiators.
@Override
public void removeInitiators(StorageSystem storage, URI exportMaskURI, List<URI> volumeURIList, List<Initiator> initiatorList, List<URI> targetURIList, TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} removeInitiators START...", storage == null ? null : storage.getSerialNumber());
String clusterName = getClusterNameFromInitiators(initiatorList);
if (clusterName == null) {
final String logMsg = "All initiators should belong to the same cluster or not have a cluster name at all";
_log.error(String.format("removeInitiator failed - maskName: %s", exportMaskURI.toString()), logMsg);
String opName = ResourceOperationTypeEnum.DELETE_EXPORT_INITIATOR.getName();
ServiceError serviceError = DeviceControllerException.errors.jobFailedOp(opName);
taskCompleter.error(_dbClient, serviceError);
return;
} else {
CloseableIterator<CIMInstance> cigInstances = null;
try {
_log.info("removeInitiators: Export mask id: {}", exportMaskURI);
if (volumeURIList != null) {
_log.info("removeInitiators: volumes : {}", Joiner.on(',').join(volumeURIList));
}
_log.info("removeInitiators: initiators : {}", Joiner.on(',').join(initiatorList));
if (targetURIList != null) {
_log.info("removeInitiators: targets : {}", Joiner.on(',').join(targetURIList));
}
boolean isRollback = WorkflowService.getInstance().isStepInRollbackState(taskCompleter.getOpId());
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(storage);
ctx.setExportMask(exportMask);
ctx.setBlockObjects(volumeURIList, _dbClient);
ctx.setInitiators(initiatorList);
// Allow exceptions to be thrown when not rolling back.
ctx.setAllowExceptions(!isRollback);
validator.removeInitiators(ctx).validate();
if (isRollback) {
// Get the context from the task completer as this is a rollback.
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(taskCompleter.getOpId());
exportMaskRollback(storage, context, taskCompleter);
} else {
CIMArgument[] inArgs;
CIMArgument[] outArgs;
_log.info("Removing initiators ...");
// Create a mapping of the InitiatorPort String to Initiator.
Map<String, Initiator> nameToInitiator = new HashMap<String, Initiator>();
for (Initiator initiator : initiatorList) {
String normalizedName = Initiator.normalizePort(initiator.getInitiatorPort());
nameToInitiator.put(normalizedName, initiator);
}
// We're going to get a mapping of which InitiatorGroups the initiators belong.
// With this mapping we can remove initiators from their associated IGs sequentially
ListMultimap<CIMObjectPath, String> igToInitiators = ArrayListMultimap.create();
mapInitiatorsToInitiatorGroups(igToInitiators, storage, initiatorList);
for (CIMObjectPath igPath : igToInitiators.keySet()) {
List<String> initiatorPorts = igToInitiators.get(igPath);
List<Initiator> initiatorsForIG = new ArrayList<Initiator>();
// Using the mapping, create a list of Initiator objects
for (String port : initiatorPorts) {
Initiator initiator = nameToInitiator.get(port);
if (initiator != null) {
initiatorsForIG.add(initiator);
}
}
boolean removingAllPortsInIG = initiatorPorts.size() == initiatorsForIG.size();
if (removingAllPortsInIG) {
// We are apparently trying to remove all the initiators in an Initiator Group.
// This is a special condition. It is not a case of removing the initiators
// from an individual group, we will instead treat this as a removal of the
// IG from the cascade-IG (thereby preventing access to the host pointed to
// by this IG).
_log.info(String.format("Request to remove all the initiators from IG %s, so we will remove the IG from the cascaded-IG", igPath.toString()));
ExportMask mask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
CIMObjectPath cigInMVPath = null;
CIMInstance mvInstance = _helper.getSymmLunMaskingView(storage, mask);
cigInstances = _helper.getAssociatorInstances(storage, mvInstance.getObjectPath(), null, SmisConstants.SE_INITIATOR_MASKING_GROUP, null, null, SmisConstants.PS_ELEMENT_NAME);
if (cigInstances.hasNext()) {
cigInMVPath = cigInstances.next().getObjectPath();
}
// Find the cascaded initiator group that this belongs to and remove the IG from it.
// Note: we should not be in here if the IG was associated directly to the MV. If the
// IG were related to the MV, then the masking orchestrator should have generated
// a workflow to delete the MV.
cigInstances = _helper.getAssociatorInstances(storage, igPath, null, SmisConstants.SE_INITIATOR_MASKING_GROUP, null, null, SmisConstants.PS_ELEMENT_NAME);
while (cigInstances.hasNext()) {
CIMObjectPath cigPath = cigInstances.next().getObjectPath();
if (!cigPath.equals(cigInMVPath)) {
// to remove the initiators from.
continue;
}
_log.info(String.format("Removing IG %s from CIG %s", igPath.toString(), cigPath.toString()));
inArgs = _helper.getRemoveIGFromCIG(igPath, cigPath);
outArgs = new CIMArgument[5];
_helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), "RemoveMembers", inArgs, outArgs, null);
// Determine if the IG contains all initiators that were added by user/ViPR, and if
// the IG is no longer referenced by masking views or parent IGs. If so, it can be
// removed.
boolean removeIG = true;
for (Initiator initiator : initiatorsForIG) {
if (!mask.hasUserInitiator(initiator.getId())) {
removeIG = false;
}
}
if (removeIG) {
List<CIMObjectPath> igList = new ArrayList<>();
igList.add(igPath);
this.checkIGsAndDeleteIfUnassociated(storage, igList);
}
}
} else {
inArgs = _helper.getRemoveInitiatorsFromMaskingGroupInputArguments(storage, igPath, initiatorsForIG);
outArgs = new CIMArgument[5];
_helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), "RemoveMembers", inArgs, outArgs, null);
}
}
if (targetURIList != null && !targetURIList.isEmpty()) {
_log.info("Removing targets...");
URI pgURI = exportMask.getPortGroup();
if (!NullColumnValueGetter.isNullURI(pgURI)) {
StoragePortGroup portGroup = _dbClient.queryObject(StoragePortGroup.class, pgURI);
if (!portGroup.getMutable()) {
_log.info(String.format("The port group %s is immutable, done", portGroup.getNativeGuid()));
taskCompleter.ready(_dbClient);
return;
}
}
CIMInstance portGroupInstance = _helper.getPortGroupInstance(storage, exportMask.getMaskName());
if (null == portGroupInstance) {
String errMsg = String.format("removeInitiators failed - maskName %s : Port group not found ", exportMask.getMaskName());
ServiceError serviceError = DeviceControllerException.errors.jobFailedMsg(errMsg, null);
taskCompleter.error(_dbClient, serviceError);
return;
}
String pgGroupName = (String) portGroupInstance.getPropertyValue(SmisConstants.CP_ELEMENT_NAME);
// Get the current ports off of the storage group; only add the ones that aren't there already.
WBEMClient client = _helper.getConnection(storage).getCimClient();
List<String> storagePorts = _helper.getStoragePortsFromLunMaskingInstance(client, portGroupInstance);
Set<URI> storagePortURIs = new HashSet<>();
storagePortURIs.addAll(transform(ExportUtils.storagePortNamesToURIs(_dbClient, storagePorts), CommonTransformerFunctions.FCTN_STRING_TO_URI));
Set<URI> portsToRemove = intersection(newHashSet(targetURIList), storagePortURIs);
boolean removingLast = portsToRemove.size() == storagePortURIs.size();
if (!portsToRemove.isEmpty() && !removingLast) {
inArgs = _helper.getRemoveTargetPortsFromMaskingGroupInputArguments(storage, pgGroupName, Lists.newArrayList(portsToRemove));
outArgs = new CIMArgument[5];
_helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), "RemoveMembers", inArgs, outArgs, null);
} else if (!removingLast) {
_log.info(String.format("Target ports already removed fom port group %s, likely by a previous operation.", pgGroupName));
} else {
// In this case, some programming, orchestration, or
// user-fiddling-with-things-outside-of-ViPR situation led us
// to this scenario.
// It's best to just print the ports and port group and leave it alone.
_log.error(String.format("Removing target ports would cause an empty port group %s, which is not allowed on VMAX. Manual port removal may be required.", pgGroupName));
// This can lead to an inaccuracy in the ExportMask object, but may be recitified next time
// it's refreshed.
}
}
}
_log.info(String.format("removeInitiators succeeded - maskName: %s", exportMaskURI.toString()));
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_log.error(String.format("removeInitiators failed - maskName: %s", exportMaskURI.toString()), e);
String opName = ResourceOperationTypeEnum.DELETE_EXPORT_INITIATOR.getName();
ServiceError serviceError = DeviceControllerException.errors.jobFailedOpMsg(opName, e.getMessage());
taskCompleter.error(_dbClient, serviceError);
} finally {
if (cigInstances != null) {
cigInstances.close();
}
}
}
_log.info("{} removeInitiators END...", storage == null ? null : storage.getSerialNumber());
}
use of com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext in project coprhd-controller by CoprHD.
the class VPlexDeviceController method storageViewAddInitiators.
/**
* Workflow Step to add initiator to Storage View.
* Note arguments (except stepId) must match storageViewAddInitiatorsMethod above.
*
* @param vplexURI
* -- URI of VPlex StorageSystem
* @param exportURI
* -- ExportGroup URI
* @param maskURI
* -- ExportMask URI. Optional.
* If non-null, only the indicated ExportMask will be processed.
* Otherwise, all ExportMasks will be processed.
* @param initiatorURIs
* -- List of initiator URIs to be added.
* @param targetURIs
* -- optional list of additional targets URIs (VPLEX FE ports) to be added.
* If non null, the targets (VPlex front end ports) indicated by the targetURIs will be added
* to the Storage View.
* @param completer the ExportMaskAddInitiatorCompleter
* @param stepId
* -- Workflow step id.
* @throws WorkflowException
*/
public void storageViewAddInitiators(URI vplexURI, URI exportURI, URI maskURI, List<URI> initiatorURIs, List<URI> targetURIs, boolean sharedExportMask, ExportMaskAddInitiatorCompleter completer, String stepId) throws DeviceControllerException {
try {
WorkflowStepCompleter.stepExecuting(stepId);
ExportOperationContext context = new VplexExportOperationContext();
// Prime the context object
completer.updateWorkflowStepContext(context);
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
ExportGroup exportGroup = getDataObject(ExportGroup.class, exportURI, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup, vplexURI);
for (ExportMask exportMask : exportMasks) {
// If a specific ExportMask is to be processed, ignore any others.
if (maskURI != null && !exportMask.getId().equals(maskURI)) {
continue;
}
_log.info("Refreshing ExportMask {}", exportMask.getMaskName());
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);
// Determine host of ExportMask
Set<URI> exportMaskHosts = VPlexUtil.getExportMaskHosts(_dbClient, exportMask, sharedExportMask);
List<Initiator> inits = _dbClient.queryObject(Initiator.class, initiatorURIs);
if (sharedExportMask) {
for (Initiator initUri : inits) {
URI hostUri = VPlexUtil.getInitiatorHost(initUri);
if (null != hostUri) {
exportMaskHosts.add(hostUri);
}
}
}
// Invoke artificial failure to simulate invalid storageview name on vplex
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_060);
// Add new targets if specified
if (targetURIs != null && targetURIs.isEmpty() == false) {
List<PortInfo> targetPortInfos = new ArrayList<PortInfo>();
List<URI> targetsAddedToStorageView = new ArrayList<URI>();
for (URI target : targetURIs) {
// Do not try to add a port twice.
if (exportMask.getStoragePorts().contains(target.toString())) {
continue;
}
// Log any ports not listed as a target in the Export Masks zoningMap
Set<String> zoningMapTargets = BlockStorageScheduler.getTargetIdsFromAssignments(exportMask.getZoningMap());
if (!zoningMapTargets.contains(target.toString())) {
_log.info(String.format("Target %s not in zoning map", target));
}
// Build the PortInfo structure for the port to be added
StoragePort port = getDataObject(StoragePort.class, target, _dbClient);
PortInfo pi = new PortInfo(port.getPortNetworkId().toUpperCase().replaceAll(":", ""), null, port.getPortName(), null);
targetPortInfos.add(pi);
targetsAddedToStorageView.add(target);
}
if (!targetPortInfos.isEmpty()) {
// Add the targets on the VPLEX
client.addTargetsToStorageView(exportMask.getMaskName(), targetPortInfos);
// Add the targets to the database.
for (URI target : targetsAddedToStorageView) {
exportMask.addTarget(target);
}
}
}
List<PortInfo> initiatorPortInfos = new ArrayList<PortInfo>();
List<String> initiatorPortWwns = new ArrayList<String>();
Map<PortInfo, Initiator> portInfosToInitiatorMap = new HashMap<PortInfo, Initiator>();
for (Initiator initiator : inits) {
// Only add this initiator if it's for the same host as other initiators in mask
if (!exportMaskHosts.contains(VPlexUtil.getInitiatorHost(initiator))) {
continue;
}
// Only add this initiator if it's not in the mask already after refresh
if (exportMask.hasInitiator(initiator.getId().toString())) {
continue;
}
PortInfo portInfo = new PortInfo(initiator.getInitiatorPort().toUpperCase().replaceAll(":", ""), initiator.getInitiatorNode().toUpperCase().replaceAll(":", ""), initiator.getLabel(), getVPlexInitiatorType(initiator));
initiatorPortInfos.add(portInfo);
initiatorPortWwns.add(initiator.getInitiatorPort());
portInfosToInitiatorMap.put(portInfo, initiator);
}
if (!initiatorPortInfos.isEmpty()) {
String lockName = null;
boolean lockAcquired = false;
try {
StringSet portIds = exportMask.getStoragePorts();
StoragePort exportMaskPort = getDataObject(StoragePort.class, URI.create(portIds.iterator().next()), _dbClient);
String clusterId = ConnectivityUtil.getVplexClusterOfPort(exportMaskPort);
lockName = _vplexApiLockManager.getLockName(vplexURI, clusterId);
lockAcquired = _vplexApiLockManager.acquireLock(lockName, LockTimeoutValue.get(LockType.VPLEX_API_LIB));
if (!lockAcquired) {
throw VPlexApiException.exceptions.couldNotObtainConcurrencyLock(vplex.getLabel());
}
// Add the initiators to the VPLEX
client.addInitiatorsToStorageView(exportMask.getMaskName(), vplexClusterName, initiatorPortInfos);
ExportOperationContext.insertContextOperation(completer, VplexExportOperationContext.OPERATION_ADD_INITIATORS_TO_STORAGE_VIEW, initiatorURIs);
} finally {
if (lockAcquired) {
_vplexApiLockManager.releaseLock(lockName);
}
}
}
}
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_003);
completer.ready(_dbClient);
} catch (VPlexApiException vae) {
_log.error("VPlexApiException adding initiator to Storage View: " + vae.getMessage(), vae);
failStep(completer, stepId, vae);
} catch (Exception ex) {
_log.error("Exception adding initiator to Storage View: " + ex.getMessage(), ex);
String opName = ResourceOperationTypeEnum.ADD_STORAGE_VIEW_INITIATOR.getName();
ServiceError serviceError = VPlexApiException.errors.storageViewAddInitiatorFailed(opName, ex);
failStep(completer, stepId, serviceError);
}
}
Aggregations