use of com.emc.storageos.vplex.api.VPlexStorageViewInfo in project coprhd-controller by CoprHD.
the class VPlexDeviceController method exportGroupRemoveVolumes.
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.impl.vplex.VplexController#exportRemoveVolume(java.net.URI, java.net.URI,
* java.net.URI,
* java.lang.String)
*/
@Override
public void exportGroupRemoveVolumes(URI vplexURI, URI exportURI, List<URI> volumeURIs, String opId) throws ControllerException {
String volListStr = "";
ExportRemoveVolumeCompleter completer = null;
boolean hasSteps = false;
try {
_log.info("entering export group remove volumes");
WorkflowStepCompleter.stepExecuting(opId);
completer = new ExportRemoveVolumeCompleter(exportURI, volumeURIs, opId);
volListStr = Joiner.on(',').join(volumeURIs);
validator.volumeURIs(volumeURIs, false, false, ValCk.ID);
Workflow workflow = _workflowService.getNewWorkflow(this, EXPORT_GROUP_REMOVE_VOLUMES, false, opId);
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
ExportGroup exportGroup = getDataObject(ExportGroup.class, exportURI, _dbClient);
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup, vplex.getId());
StringBuffer errorMessages = new StringBuffer();
boolean isValidationNeeded = validatorConfig.isValidationEnabled();
_log.info("Orchestration level validation needed : {}", isValidationNeeded);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
// For safety, run remove volumes serially
String previousStep = null;
for (ExportMask exportMask : exportMasks) {
if (exportMask.getInactive()) {
_log.info(String.format("ExportMask %s (%s) is inactive, skipping", exportMask.getMaskName(), exportMask.getId()));
continue;
}
String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
Map<String, String> targetPortToPwwnMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
_log.info("Refreshing ExportMask {}", exportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortToPwwnMap, _networkDeviceController);
List<URI> volumeURIList = new ArrayList<URI>();
List<URI> remainingVolumesInMask = new ArrayList<URI>();
if (exportMask.getVolumes() != null && !exportMask.getVolumes().isEmpty()) {
// note that this is the assumed behavior even for the
// situation in which this export mask is in use by other
// export groups... see CTRL-3941
// assemble a list of other ExportGroups that reference this ExportMask
List<ExportGroup> otherExportGroups = ExportUtils.getOtherExportGroups(exportGroup, exportMask, _dbClient);
if (otherExportGroups != null && !otherExportGroups.isEmpty()) {
// Gets the list of volume URIs that are not other Export Groups
volumeURIList = getVolumeListDiff(exportGroup, exportMask, otherExportGroups, volumeURIs);
} else {
volumeURIList = volumeURIs;
}
Map<URI, BlockObject> blockObjectMap = VPlexUtil.translateRPSnapshots(_dbClient, volumeURIList);
volumeURIList.clear();
volumeURIList.addAll(blockObjectMap.keySet());
volumeURIList = ExportMaskUtils.filterVolumesByExportMask(volumeURIList, exportMask);
for (String volumeURI : exportMask.getVolumes().keySet()) {
remainingVolumesInMask.add(URI.create(volumeURI));
}
remainingVolumesInMask.removeAll(volumeURIList);
}
_log.info(String.format("exportGroupRemove: mask %s volumes to process: %s", exportMask.getMaskName(), volumeURIList.toString()));
// Fetch exportMask again as exportMask zoning Map might have changed in zoneExportRemoveVolumes
exportMask = _dbClient.queryObject(ExportMask.class, exportMask.getId());
boolean existingVolumes = exportMask.hasAnyExistingVolumes();
// The useradded - existing initiators enhancement made in refreshExportMask
// guarantees that all the discovered initiators are added to userAdded,
// irrespective of whether it's already registered on the array manually.
boolean existingInitiators = exportMask.hasAnyExistingInitiators();
boolean canMaskBeDeleted = remainingVolumesInMask.isEmpty() && !existingInitiators && !existingVolumes;
if (!canMaskBeDeleted) {
_log.info("this mask is not empty, so just updating: " + exportMask.getMaskName());
completer.addExportMaskToRemovedVolumeMapping(exportMask.getId(), volumeURIList);
Workflow.Method storageViewRemoveVolume = storageViewRemoveVolumesMethod(vplex.getId(), exportGroup.getId(), exportMask.getId(), volumeURIList, opId, completer, null);
previousStep = workflow.createStep("removeVolumes", String.format("Removing volumes from export on storage array %s (%s) for export mask %s (%s)", vplex.getNativeGuid(), vplex.getId().toString(), exportMask.getMaskName(), exportMask.getId()), previousStep, vplex.getId(), vplex.getSystemType(), this.getClass(), storageViewRemoveVolume, rollbackMethodNullMethod(), null);
// Add zoning step for removing volumes
List<NetworkZoningParam> zoningParam = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportGroup.getId(), Collections.singletonList(exportMask.getId()), _dbClient);
Workflow.Method zoneRemoveVolumesMethod = _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParam, volumeURIList);
previousStep = workflow.createStep(null, "Zone remove volumes mask: " + exportMask.getMaskName(), previousStep, nullURI, "network-system", _networkDeviceController.getClass(), zoneRemoveVolumesMethod, _networkDeviceController.zoneNullRollbackMethod(), null);
hasSteps = true;
/**
* TODO
* ExportremoveVolumeCompleter should be enhanced to remove export mask from export group if last volume removed.
* We already take care of this.
*/
} else {
_log.info("this mask is empty of ViPR-managed volumes, and there are no existing volumes or initiators, so deleting: " + exportMask.getMaskName());
List<NetworkZoningParam> zoningParams = NetworkZoningParam.convertExportMasksToNetworkZoningParam(exportURI, Collections.singletonList(exportMask.getId()), _dbClient);
hasSteps = true;
String exportMaskDeleteStep = workflow.createStepId();
Workflow.Method deleteStorageView = deleteStorageViewMethod(vplexURI, exportURI, exportMask.getId(), false);
previousStep = workflow.createStep(DELETE_STORAGE_VIEW, String.format("Deleting storage view: %s (%s)", exportMask.getMaskName(), exportMask.getId()), previousStep, vplexURI, vplex.getSystemType(), this.getClass(), deleteStorageView, null, exportMaskDeleteStep);
// Unzone step (likely just a FCZoneReference removal since this is remove volume only)
previousStep = workflow.createStep(ZONING_STEP, "Zoning subtask for export-group: " + exportURI, previousStep, NullColumnValueGetter.getNullURI(), "network-system", _networkDeviceController.getClass(), _networkDeviceController.zoneExportRemoveVolumesMethod(zoningParams, volumeURIs), null, workflow.createStepId());
}
}
if (hasSteps) {
String message = errorMessages.toString();
if (isValidationNeeded && !message.isEmpty()) {
_log.error("Error Message {}", errorMessages);
List<Volume> volumes = _dbClient.queryObject(Volume.class, volumeURIs);
String volumesForDisplay = Joiner.on(", ").join(Collections2.transform(volumes, CommonTransformerFunctions.fctnDataObjectToForDisplay()));
throw DeviceControllerException.exceptions.removeVolumesValidationError(volumesForDisplay, vplex.forDisplay(), message);
}
workflow.executePlan(completer, String.format("Sucessfully removed volumes or deleted Storage View: %s (%s)", exportGroup.getLabel(), exportGroup.getId()));
} else {
completer.ready(_dbClient);
}
} catch (VPlexApiException vae) {
String message = String.format("Failed to remove Volume %s from ExportGroup %s", volListStr, exportURI);
_log.error(message, vae);
failStep(completer, opId, vae);
} catch (Exception ex) {
String message = String.format("Failed to remove Volume %s from ExportGroup %s", volListStr, exportURI);
_log.error(message, ex);
String opName = ResourceOperationTypeEnum.DELETE_EXPORT_VOLUME.getName();
ServiceError serviceError = VPlexApiException.errors.exportGroupRemoveVolumesFailed(volListStr, exportURI.toString(), opName, ex);
failStep(completer, opId, serviceError);
}
}
use of com.emc.storageos.vplex.api.VPlexStorageViewInfo in project coprhd-controller by CoprHD.
the class VPlexDeviceController method assembleExportMasksWorkflow.
/**
* Method to assemble the find or create VPLEX storage views (export masks) workflow for the given
* ExportGroup, Initiators, and the block object Map.
*
* @param vplexURI
* the URI of the VPLEX StorageSystem object
* @param export
* the ExportGroup in question
* @param varrayUri
* -- NOTE! The varrayURI may NOT be the same as the exportGroup varray!.
* @param initiators
* if initiators is null, the method will use all initiators from the ExportGroup
* @param blockObjectMap
* the key (URI) of this map can reference either the volume itself or a snapshot.
* @param zoningStepNeeded - Determines whether zone step is needed
* @param workflow
* the controller workflow
* @param waitFor
* -- If non-null, will wait on previous workflow step
* @param opId
* the workflow step id
* @return the last Workflow Step id
* @throws Exception
*/
private String assembleExportMasksWorkflow(URI vplexURI, URI export, URI varrayUri, List<URI> initiators, Map<URI, Integer> blockObjectMap, boolean zoningStepNeeded, Workflow workflow, String waitFor, String opId) throws Exception {
long startAssembly = new Date().getTime();
VPlexControllerUtils.cleanStaleExportMasks(_dbClient, vplexURI);
_log.info("TIMER: clean stale export masks took {} ms", new Date().getTime() - startAssembly);
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplexURI, _dbClient);
ExportGroup exportGroup = getDataObject(ExportGroup.class, export, _dbClient);
// filter volume map just for this VPLEX's virtual volumes
_log.info("object map before filtering for this VPLEX: " + blockObjectMap);
Map<URI, Integer> filteredBlockObjectMap = new HashMap<URI, Integer>();
for (URI boURI : blockObjectMap.keySet()) {
BlockObject bo = Volume.fetchExportMaskBlockObject(_dbClient, boURI);
if (bo.getStorageController().equals(vplexURI)) {
filteredBlockObjectMap.put(bo.getId(), blockObjectMap.get(boURI));
}
}
blockObjectMap = filteredBlockObjectMap;
_log.info("object map after filtering for this VPLEX: " + blockObjectMap);
// validate volume to lun map to make sure there aren't any duplicate LUN entries
ExportUtils.validateExportGroupVolumeMap(exportGroup.getLabel(), blockObjectMap);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplexSystem, _dbClient);
// we will need lists of new export masks to create and existing export masks to simply update
List<ExportMask> exportMasksToCreateOnDevice = new ArrayList<ExportMask>();
List<ExportMask> exportMasksToUpdateOnDevice = new ArrayList<ExportMask>();
// In case we are just updating the existing export masks we will need this map
// to add initiators which are not already there.
Map<URI, List<Initiator>> exportMasksToUpdateOnDeviceWithInitiators = new HashMap<URI, List<Initiator>>();
// In case we are just updating the existing export masks we will need this map
// to add storage ports which are not already there.
Map<URI, List<URI>> exportMasksToUpdateOnDeviceWithStoragePorts = new HashMap<URI, List<URI>>();
// try to collect initiators, if none supplied by caller
if (initiators == null) {
initiators = new ArrayList<URI>();
if (exportGroup.hasInitiators()) {
for (String initiator : exportGroup.getInitiators()) {
initiators.add(URI.create(initiator));
}
}
}
// sort initiators in a host to initiator map
Map<URI, List<Initiator>> hostInitiatorMap = VPlexUtil.makeHostInitiatorsMap(initiators, _dbClient);
// This Set will be used to track shared export mask in database.
Set<ExportMask> sharedExportMasks = new HashSet<ExportMask>();
// look at each host
String lockName = null;
boolean lockAcquired = false;
String vplexClusterId = ConnectivityUtil.getVplexClusterForVarray(varrayUri, vplexURI, _dbClient);
String vplexClusterName = VPlexUtil.getVplexClusterName(varrayUri, vplexURI, client, _dbClient);
try {
lockName = _vplexApiLockManager.getLockName(vplexURI, vplexClusterId);
lockAcquired = _vplexApiLockManager.acquireLock(lockName, LockTimeoutValue.get(LockType.VPLEX_API_LIB));
if (!lockAcquired) {
throw VPlexApiException.exceptions.couldNotObtainConcurrencyLock(vplexSystem.getLabel());
}
Map<String, String> targetPortToPwwnMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
Boolean[] doInitiatorRefresh = new Boolean[] { new Boolean(true) };
/**
* COP-28674: During Vblock boot volume export, if existing storage views are found then check for existing volumes
* If found throw exception. This condition is valid only for boot volume vblock export.
*/
if (exportGroup.forHost() && ExportMaskUtils.isVblockHost(initiators, _dbClient) && ExportMaskUtils.isBootVolume(_dbClient, blockObjectMap)) {
_log.info("VBlock boot volume Export: Validating the Vplex Cluster {} to find existing storage views", vplexClusterName);
List<Initiator> initiatorList = _dbClient.queryObject(Initiator.class, initiators);
List<String> initiatorNames = getInitiatorNames(vplexSystem.getSerialNumber(), vplexClusterName, client, initiatorList.iterator(), doInitiatorRefresh);
List<VPlexStorageViewInfo> storageViewInfos = client.getStorageViewsContainingInitiators(vplexClusterName, initiatorNames);
List<String> maskNames = new ArrayList<String>();
if (!CollectionUtils.isEmpty(storageViewInfos)) {
for (VPlexStorageViewInfo storageView : storageViewInfos) {
if (!CollectionUtils.isEmpty(storageView.getVirtualVolumes())) {
maskNames.add(storageView.getName());
} else {
_log.info("Found Storage View {} for cluster {} with empty volumes", storageView.getName(), vplexClusterName);
}
}
} else {
_log.info("No Storage views found for cluster {}", vplexClusterName);
}
if (!CollectionUtils.isEmpty(maskNames)) {
Set<URI> computeResourceSet = hostInitiatorMap.keySet();
throw VPlexApiException.exceptions.existingMaskFoundDuringBootVolumeExport(Joiner.on(", ").join(maskNames), Joiner.on(", ").join(computeResourceSet), vplexClusterName);
} else {
_log.info("VBlock Boot volume Export : Validation passed");
}
}
for (URI hostUri : hostInitiatorMap.keySet()) {
_log.info("assembling export masks workflow, now looking at host URI: " + hostUri);
List<Initiator> inits = hostInitiatorMap.get(hostUri);
_log.info("this host contains these initiators: " + inits);
boolean foundMatchingStorageView = false;
boolean allPortsFromMaskMatchForVarray = true;
_log.info("attempting to locate an existing ExportMask for this host's initiators on VPLEX Cluster " + vplexClusterId);
Map<URI, ExportMask> vplexExportMasks = new HashMap<URI, ExportMask>();
allPortsFromMaskMatchForVarray = filterExportMasks(vplexExportMasks, inits, varrayUri, vplexSystem, vplexClusterId);
ExportMask sharedVplexExportMask = null;
switch(vplexExportMasks.size()) {
case FOUND_NO_VIPR_EXPORT_MASKS:
// Didn't find any single existing ExportMask for this Host/Initiators.
// Check if there is a shared ExportMask in CoprHD with by checking the existing initiators list.
sharedVplexExportMask = VPlexUtil.getExportMasksWithExistingInitiators(vplexURI, _dbClient, inits, varrayUri, vplexClusterId);
// on the VPLEX and set it up as a new ExportMask.
if (null != sharedVplexExportMask) {
sharedExportMasks.add(sharedVplexExportMask);
// If sharedVplexExportMask is found then that export mask will be used to add any missing
// initiators or storage ports as needed, and volumes requested, if not already present.
setupExistingExportMaskWithNewHost(blockObjectMap, vplexSystem, exportGroup, varrayUri, exportMasksToUpdateOnDevice, exportMasksToUpdateOnDeviceWithInitiators, exportMasksToUpdateOnDeviceWithStoragePorts, inits, sharedVplexExportMask, opId);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, sharedVplexExportMask.getMaskName());
_log.info("Refreshing ExportMask {}", sharedVplexExportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, sharedVplexExportMask, targetPortToPwwnMap, _networkDeviceController);
foundMatchingStorageView = true;
break;
} else {
_log.info("could not find an existing matching ExportMask in ViPR, " + "so ViPR will see if there is one already on the VPLEX system");
// ViPR will attempt to find a suitable existing storage view
// on the VPLEX and set it up as a new ExportMask.
long start = new Date().getTime();
foundMatchingStorageView = checkForExistingStorageViews(client, targetPortToPwwnMap, vplexSystem, vplexClusterName, inits, exportGroup, varrayUri, blockObjectMap, exportMasksToUpdateOnDevice, exportMasksToUpdateOnDeviceWithInitiators, exportMasksToUpdateOnDeviceWithStoragePorts, doInitiatorRefresh, opId);
long elapsed = new Date().getTime() - start;
_log.info("TIMER: finding an existing storage view took {} ms and returned {}", elapsed, foundMatchingStorageView);
break;
}
case FOUND_ONE_VIPR_EXPORT_MASK:
// get the single value in the map
ExportMask viprExportMask = vplexExportMasks.values().iterator().next();
_log.info("a valid ExportMask matching these initiators exists already in ViPR " + "for this VPLEX device, so ViPR will re-use it: " + viprExportMask.getMaskName());
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, viprExportMask.getMaskName());
_log.info("Refreshing ExportMask {}", viprExportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, viprExportMask, targetPortToPwwnMap, _networkDeviceController);
reuseExistingExportMask(blockObjectMap, vplexSystem, exportGroup, varrayUri, exportMasksToUpdateOnDevice, exportMasksToUpdateOnDeviceWithStoragePorts, inits, allPortsFromMaskMatchForVarray, viprExportMask, opId);
foundMatchingStorageView = true;
break;
default:
String message = "Invalid Configuration: more than one VPLEX ExportMask " + "in this cluster exists in ViPR for these initiators " + inits;
_log.error(message);
throw new Exception(message);
}
// or set up a brand new ExportMask (to create a storage view on the VPLEX) for the Host.
if (!foundMatchingStorageView) {
if (null == sharedVplexExportMask) {
// If we reached here, it means there isn't an existing storage view on the VPLEX with the host,
// and no other shared ExportMask was found in the database by looking at existingInitiators.
// So, ViPR will try to find if there is shared export mask already for the ExportGroup in the database.
sharedVplexExportMask = VPlexUtil.getSharedExportMaskInDb(exportGroup, vplexURI, _dbClient, varrayUri, vplexClusterId, hostInitiatorMap);
}
if (null != sharedVplexExportMask) {
// If a sharedExportMask was found, then the new host will be added to that ExportMask
_log.info(String.format("Shared export mask %s %s found for the export group %s %s which will be reused for initiators %s .", sharedVplexExportMask.getMaskName(), sharedVplexExportMask.getId(), exportGroup.getLabel(), exportGroup.getId(), inits.toString()));
sharedExportMasks.add(sharedVplexExportMask);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, sharedVplexExportMask.getMaskName());
_log.info("Refreshing ExportMask {}", sharedVplexExportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, sharedVplexExportMask, targetPortToPwwnMap, _networkDeviceController);
setupExistingExportMaskWithNewHost(blockObjectMap, vplexSystem, exportGroup, varrayUri, exportMasksToUpdateOnDevice, exportMasksToUpdateOnDeviceWithInitiators, exportMasksToUpdateOnDeviceWithStoragePorts, inits, sharedVplexExportMask, opId);
} else {
// Otherwise, create a brand new ExportMask, which will enable the storage view to
// be created on the VPLEX device as part of this workflow
_log.info("did not find a matching existing storage view anywhere, so ViPR " + "will initialize a new one and push it to the VPLEX device");
setupNewExportMask(blockObjectMap, vplexSystem, exportGroup, varrayUri, exportMasksToCreateOnDevice, inits, vplexClusterId, opId);
}
}
}
} finally {
if (lockAcquired) {
_vplexApiLockManager.releaseLock(lockName);
}
}
_log.info("updating zoning if necessary for both new and updated export masks");
String zoningStepId = waitFor;
if (zoningStepNeeded) {
_log.info("Adding step to update zones if required.");
zoningStepId = addStepsForZoningUpdate(export, initiators, blockObjectMap, workflow, waitFor, exportMasksToCreateOnDevice, exportMasksToUpdateOnDevice);
}
_log.info("set up initiator pre-registration step");
String storageViewStepId = addStepsForInitiatorRegistration(initiators, vplexSystem, vplexClusterName, workflow, zoningStepId);
_log.info("processing the export masks to be created");
for (ExportMask exportMask : exportMasksToCreateOnDevice) {
storageViewStepId = addStepsForExportMaskCreate(blockObjectMap, workflow, vplexSystem, exportGroup, storageViewStepId, exportMask);
}
_log.info("processing the export masks to be updated");
for (ExportMask exportMask : exportMasksToUpdateOnDevice) {
boolean shared = false;
if (sharedExportMasks.contains(exportMask)) {
shared = true;
}
storageViewStepId = addStepsForExportMaskUpdate(export, blockObjectMap, workflow, vplexSystem, exportMasksToUpdateOnDeviceWithInitiators, exportMasksToUpdateOnDeviceWithStoragePorts, storageViewStepId, exportMask, shared);
}
long elapsed = new Date().getTime() - startAssembly;
_log.info("TIMER: export mask assembly took {} ms", elapsed);
return storageViewStepId;
}
use of com.emc.storageos.vplex.api.VPlexStorageViewInfo in project coprhd-controller by CoprHD.
the class VPlexDeviceController method addStepsForAddInitiators.
/**
* Add workflow steps for adding Initiators to a specific varray for the given VPlex.
*
* @param workflow
* -- Workflow steps go into
* @param vplex
* -- Storage system
* @param exportGroup
* -- ExportGroup operation invoked on
* @param varrayURI
* -- Virtual Array URI that the Initiators are in
* @param hostInitiatorURIs
* -- URIs of the Initiators
* @param initiators
* -- list of Initiator objects
* @param hostURI
* -- The hostURI
* @param previousStepId
* -- wait on this step if non-null
* @param opId
* -- step id for our operation
* @return StepId of last step generated
*/
private String addStepsForAddInitiators(Workflow workflow, StorageSystem vplex, ExportGroup exportGroup, URI varrayURI, List<URI> hostInitiatorURIs, List<Initiator> initiators, URI hostURI, String previousStepId, String opId) throws Exception {
String lastStepId = null;
URI vplexURI = vplex.getId();
URI exportURI = exportGroup.getId();
String initListStr = Joiner.on(',').join(hostInitiatorURIs);
// Find the ExportMask for my host.
ExportMask exportMask = VPlexUtil.getExportMaskForHostInVarray(_dbClient, exportGroup, hostURI, vplexURI, varrayURI);
if (exportMask == null) {
_log.info("No export mask found for hostURI: " + hostURI + " varrayURI: " + varrayURI);
Map<URI, Integer> volumeMap = ExportUtils.getExportGroupVolumeMap(_dbClient, vplex, exportGroup);
// Partition the Volumes by varray.
Map<URI, Set<URI>> varrayToVolumes = VPlexUtil.mapBlockObjectsToVarrays(_dbClient, volumeMap.keySet(), vplexURI, exportGroup);
// Filter the volumes by our Varray.
Map<URI, Integer> varrayVolumeMap = ExportMaskUtils.filterVolumeMap(volumeMap, varrayToVolumes.get(varrayURI));
// Create the ExportMask if there are volumes in this varray.
if (!varrayVolumeMap.isEmpty()) {
lastStepId = assembleExportMasksWorkflow(vplexURI, exportURI, varrayURI, hostInitiatorURIs, varrayVolumeMap, true, workflow, previousStepId, opId);
}
} else {
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
_log.info("Refreshing ExportMask {}", exportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName), _networkDeviceController);
if (exportMask.getVolumes() == null) {
// This can occur in Brownfield scenarios where we have not added any volumes yet to the HA side,
// CTRL10760
_log.info(String.format("No volumes in ExportMask %s (%s), so not adding initiators", exportMask.getMaskName(), exportMask.getId()));
return lastStepId;
}
_log.info(String.format("Adding initiators %s for host %s mask %s (%s)", getInitiatorsWwnsString(initiators), hostURI.toString(), exportMask.getMaskName(), exportMask.getId()));
// Calculate the path parameters for the volumes in this ExportMask
Collection<URI> volumeURIs = new HashSet<URI>();
if (exportMask.getVolumes() != null && !exportMask.getVolumes().isEmpty()) {
volumeURIs = (Collections2.transform(exportMask.getVolumes().keySet(), CommonTransformerFunctions.FCTN_STRING_TO_URI));
} else if (exportGroup.getVolumes() != null && !exportGroup.getVolumes().isEmpty()) {
// Hit this condition
// in CTRL-9944
// (unknown why)
_log.info(String.format("No volumes in ExportMask %s, using ExportGroup %s for ExportPathParam", exportMask.getId(), exportGroup.getId()));
Map<URI, Integer> volumeMap = ExportUtils.getExportGroupVolumeMap(_dbClient, vplex, exportGroup);
// Partition the Volumes by varray. Then use only the volumes in the requested varray.
Map<URI, Set<URI>> varrayToVolumes = VPlexUtil.mapBlockObjectsToVarrays(_dbClient, volumeMap.keySet(), vplexURI, exportGroup);
volumeURIs = varrayToVolumes.get(varrayURI);
} else {
_log.info(String.format("No volumes at all- using default path parameters: %s", exportMask.getId()));
}
ExportPathParams pathParams = _blockScheduler.calculateExportPathParamForVolumes(volumeURIs, exportGroup.getNumPaths(), exportMask.getStorageDevice(), exportGroup.getId());
if (exportGroup.getType() != null) {
pathParams.setExportGroupType(exportGroup.getType());
}
// Assign additional StoragePorts if needed.
Map<URI, List<URI>> assignments = _blockScheduler.assignStoragePorts(vplex, exportGroup, initiators, exportMask.getZoningMap(), pathParams, volumeURIs, _networkDeviceController, varrayURI, opId);
List<URI> newTargetURIs = BlockStorageScheduler.getTargetURIsFromAssignments(assignments);
// Build a list of StoragePort targets to remove during rollback. Do not remove existing storage ports during rollback.
List<URI> rollbackTargetURIs = new ArrayList<URI>();
for (URI target : newTargetURIs) {
if (exportMask.getStoragePorts().contains(target.toString())) {
// Skip the target port if it exists in the ViPR ExportMask
continue;
}
rollbackTargetURIs.add(target);
}
exportMask.addZoningMap(BlockStorageScheduler.getZoneMapFromAssignments(assignments));
_dbClient.updateObject(exportMask);
_log.info(String.format("Adding targets %s for host %s", newTargetURIs.toString(), hostURI.toString()));
// Create a Step to add the SAN Zone
String zoningStepId = workflow.createStepId();
Workflow.Method zoningMethod = zoneAddInitiatorStepMethod(vplexURI, exportURI, hostInitiatorURIs, varrayURI);
Workflow.Method zoningRollbackMethod = zoneRollbackMethod(exportURI, zoningStepId);
zoningStepId = workflow.createStep(ZONING_STEP, String.format("Zone initiator %s to ExportGroup %s(%s)", initListStr, exportGroup.getLabel(), exportURI), previousStepId, vplexURI, vplex.getSystemType(), this.getClass(), zoningMethod, zoningRollbackMethod, zoningStepId);
// Create a Step to add the initiator to the Storage View
String message = String.format("initiators %s to StorageView %s", initListStr, exportGroup.getGeneratedName());
ExportMask sharedExportMask = VPlexUtil.getSharedExportMaskInDb(exportGroup, vplexURI, _dbClient, varrayURI, null, null);
boolean shared = false;
if (null != sharedExportMask && sharedExportMask.getId().equals(exportMask.getId())) {
shared = true;
}
String addInitStep = workflow.createStepId();
ExportMaskAddInitiatorCompleter addInitCompleter = new ExportMaskAddInitiatorCompleter(exportURI, exportMask.getId(), hostInitiatorURIs, newTargetURIs, addInitStep);
Workflow.Method addToViewMethod = storageViewAddInitiatorsMethod(vplexURI, exportURI, exportMask.getId(), hostInitiatorURIs, newTargetURIs, shared, addInitCompleter);
Workflow.Method addToViewRollbackMethod = storageViewAddInitiatorsRollbackMethod(vplexURI, exportURI, exportMask.getId(), hostInitiatorURIs, rollbackTargetURIs, addInitStep);
lastStepId = workflow.createStep("storageView", "Add " + message, zoningStepId, vplexURI, vplex.getSystemType(), this.getClass(), addToViewMethod, addToViewRollbackMethod, addInitStep);
}
return lastStepId;
}
use of com.emc.storageos.vplex.api.VPlexStorageViewInfo in project coprhd-controller by CoprHD.
the class VPlexDeviceController method portRebalance.
@Override
public void portRebalance(URI vplex, URI exportGroupURI, URI varray, URI exportMaskURI, Map<URI, List<URI>> adjustedPaths, Map<URI, List<URI>> removedPaths, boolean isAdd, String stepId) throws Exception {
// Retrieve the ExportGroup and ExportMask and validate
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
if (exportGroup == null || exportMask == null || exportGroup.getInactive() || exportMask.getInactive() || !exportGroup.hasMask(exportMaskURI)) {
String reason = String.format("Bad exportGroup %s or exportMask %s", exportGroupURI, exportMaskURI);
_log.error(reason);
ServiceCoded coded = WorkflowException.exceptions.workflowConstructionError(reason);
WorkflowStepCompleter.stepFailed(stepId, coded);
return;
}
// Check if the ExportMask is in the desired varray (in cross-coupled ExportGroups)
if (!ExportMaskUtils.exportMaskInVarray(_dbClient, exportMask, varray)) {
_log.info(String.format("ExportMask %s (%s) not in specified varray %s", exportMask.getMaskName(), exportMask.getId(), varray));
WorkflowStepCompleter.stepSucceeded(stepId, String.format("No operation done: Mask not in specified varray %s", varray));
return;
}
// Refresh the ExportMask so we have the latest data.
StorageSystem vplexSystem = getDataObject(StorageSystem.class, vplex, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplex, client, _dbClient);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
_log.info("Processing and Refreshing ExportMask {}", exportMask.getMaskName());
Map<String, String> targetPortToPwwnMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortToPwwnMap, _networkDeviceController);
// Determine hosts in ExportMask
Set<URI> hostsInExportMask = new HashSet<URI>();
Set<String> hostNames = ExportMaskUtils.getHostNamesInMask(exportMask, _dbClient);
Set<Initiator> initiatorsInMask = ExportMaskUtils.getInitiatorsForExportMask(_dbClient, exportMask, null);
for (Initiator initiator : initiatorsInMask) {
if (initiator.getHost() != null) {
hostsInExportMask.add(initiator.getHost());
}
}
boolean sharedMask = (hostsInExportMask.size() > 1);
if (isAdd) {
// Processing added paths only
Workflow workflow = _workflowService.getNewWorkflow(this, "portRebalance", false, stepId);
// Determine initiators and targets to be added.
// These are initiators not in mask that are in a host in the mask.
// Earlier versions of the Vplex code may not have had all the initiators in the mask, as
// earlier code only put initiators in the Storage View for which ports were added.
// Targets to be added may be on existing initiators or newly added initiators.
List<URI> initiatorsToAdd = new ArrayList<URI>();
List<URI> targetsToAdd = new ArrayList<URI>();
for (URI initiatorURI : adjustedPaths.keySet()) {
if (!exportMask.hasInitiator(initiatorURI.toString())) {
// Initiator not in ExportMask
Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI);
if (initiator != null && !initiator.getInactive()) {
if (hostsInExportMask.contains(initiator.getHost())) {
initiatorsToAdd.add(initiatorURI);
for (URI targetURI : adjustedPaths.get(initiatorURI)) {
if (!exportMask.hasTargets(Arrays.asList(targetURI)) && !targetsToAdd.contains(targetURI)) {
targetsToAdd.add(targetURI);
}
}
}
}
} else {
// Initiator already in ExportMask, look for additional targets
for (URI targetURI : adjustedPaths.get(initiatorURI)) {
if (!exportMask.hasTargets(Arrays.asList(targetURI)) && !targetsToAdd.contains(targetURI)) {
targetsToAdd.add(targetURI);
}
}
}
}
_log.info("Targets to add: " + targetsToAdd.toString());
_log.info("Initiators to add: " + initiatorsToAdd.toString());
// Invoke either storageViewAddInitiators if there are initiators to be added (it will add targets also),
// or storageViewAddStoragePorts if no initiators to be added (which adds only targets).
Workflow.Method addPathsMethod = null;
if (!initiatorsToAdd.isEmpty() || !targetsToAdd.isEmpty()) {
String addInitiatorStepId = workflow.createStepId();
ExportMaskAddInitiatorCompleter completer = new ExportMaskAddInitiatorCompleter(exportGroupURI, exportMaskURI, initiatorsToAdd, targetsToAdd, addInitiatorStepId);
;
if (!initiatorsToAdd.isEmpty()) {
addPathsMethod = storageViewAddInitiatorsMethod(vplex, exportGroupURI, exportMaskURI, initiatorsToAdd, targetsToAdd, sharedMask, completer);
} else if (!targetsToAdd.isEmpty()) {
addPathsMethod = storageViewAddStoragePortsMethod(vplex, exportGroupURI, exportMaskURI, targetsToAdd, completer);
}
String description = String.format("Adding paths to ExportMask %s Hosts %s", exportMask.getMaskName(), hostNames.toString());
workflow.createStep("addPaths", description, null, vplex, vplexSystem.getSystemType(), this.getClass(), addPathsMethod, null, false, addInitiatorStepId);
ExportMaskAddPathsCompleter workflowCompleter = new ExportMaskAddPathsCompleter(exportGroupURI, exportMaskURI, stepId);
workflow.executePlan(workflowCompleter, description + " completed successfully");
return;
}
} else {
// Processing the paths to be removed only, Paths not in the removedPaths map will be retained.
// Note that we only remove ports (targets), never initiators.
Workflow workflow = _workflowService.getNewWorkflow(this, "portRebalance", false, stepId);
// Compute the targets to be removed.
Set<URI> targetsToBeRemoved = ExportMaskUtils.getAllPortsInZoneMap(removedPaths);
Collection<URI> targetsInMask = Collections2.transform(exportMask.getStoragePorts(), CommonTransformerFunctions.FCTN_STRING_TO_URI);
targetsToBeRemoved.retainAll(targetsInMask);
Set<URI> targetsToBeRetained = ExportMaskUtils.getAllPortsInZoneMap(adjustedPaths);
targetsToBeRemoved.removeAll(targetsToBeRetained);
List<URI> portsToBeRemoved = new ArrayList<URI>(targetsToBeRemoved);
_log.info("Targets to be removed: " + portsToBeRemoved.toString());
// Call storageViewRemoveStoragePorts to remove any necessary targets.
Workflow.Method removePathsMethod = null;
if (!portsToBeRemoved.isEmpty()) {
String removeInitiatorStepId = workflow.createStepId();
removePathsMethod = storageViewRemoveStoragePortsMethod(vplex, exportGroupURI, exportMaskURI, portsToBeRemoved, null);
String description = String.format("Removing paths to ExportMask %s Hosts %s", exportMask.getMaskName(), hostNames.toString());
workflow.createStep("removePaths", description, null, vplex, vplexSystem.getSystemType(), this.getClass(), removePathsMethod, null, false, removeInitiatorStepId);
ExportMaskRemovePathsCompleter workflowCompleter = new ExportMaskRemovePathsCompleter(exportGroupURI, exportMaskURI, stepId);
workflowCompleter.setRemovedStoragePorts(portsToBeRemoved);
workflow.executePlan(workflowCompleter, description + " completed successfully");
return;
}
}
// Apparently nothing to do, return success
WorkflowStepCompleter.stepSucceeded(stepId, "No operation performed on VPLEX mask");
}
use of com.emc.storageos.vplex.api.VPlexStorageViewInfo in project coprhd-controller by CoprHD.
the class VPlexDeviceController method exportGroupRemoveInitiators.
/*
* (non-Javadoc)
*
* @see com.emc.storageos.volumecontroller.impl.vplex.VplexController#exportRemoveInitiator(java.net.URI,
* java.net.URI, java.net.URI,
* java.lang.String)
*/
@Override
public void exportGroupRemoveInitiators(URI vplexURI, URI exportURI, List<URI> initiatorURIs, String opId) throws ControllerException {
try {
StorageSystem vplex = getDataObject(StorageSystem.class, vplexURI, _dbClient);
ExportGroup exportGroup = getDataObject(ExportGroup.class, exportURI, _dbClient);
ExportRemoveInitiatorCompleter completer = new ExportRemoveInitiatorCompleter(exportURI, initiatorURIs, opId);
Workflow workflow = _workflowService.getNewWorkflow(this, "exportRemoveInitiator", true, opId);
// true if Workflow has a Step
boolean hasStep = false;
Initiator firstInitiator = _dbClient.queryObject(Initiator.class, initiatorURIs.get(0));
StringBuffer errorMessages = new StringBuffer();
boolean isValidationNeeded = validatorConfig.isValidationEnabled() && !ExportUtils.checkIfInitiatorsForRP(Arrays.asList(firstInitiator));
_log.info("Orchestration level validation needed : {}", isValidationNeeded);
_log.info("starting remove initiators for export group: " + exportGroup.toString());
_log.info("request is to remove these initiators: " + initiatorURIs);
initiatorURIs = VPlexUtil.filterInitiatorsForVplex(_dbClient, initiatorURIs);
// get a map of host URI to a list of Initiators in that Host
Map<URI, List<Initiator>> hostInitiatorsMap = VPlexUtil.makeHostInitiatorsMap(initiatorURIs, _dbClient);
VPlexApiClient client = getVPlexAPIClient(_vplexApiFactory, vplex, _dbClient);
// Loop, processing each host separately.
for (URI hostURI : hostInitiatorsMap.keySet()) {
_log.info("setting up initiator removal for host " + hostURI);
// Get the initiators (and initiator URIs) for this host
List<Initiator> initiators = hostInitiatorsMap.get(hostURI);
// Find the ExportMask for my host.
List<ExportMask> exportMasks = getExportMaskForHost(exportGroup, hostURI, vplexURI);
if (exportMasks == null || exportMasks.isEmpty()) {
// If there is no ExportMask for this host, there is nothing to do.
_log.info("No export mask found for hostURI: " + hostURI);
continue;
}
String lastStep = null;
List<URI> initiatorsAlreadyRemovedFromExportGroup = new ArrayList<URI>();
for (ExportMask exportMask : exportMasks) {
_log.info("adding remove initiators steps for " + "export mask / storage view: " + exportMask.getMaskName());
String vplexClusterName = VPlexUtil.getVplexClusterName(exportMask, vplexURI, client, _dbClient);
Map<String, String> targetPortToPwwnMap = VPlexControllerUtils.getTargetPortToPwwnMap(client, vplexClusterName);
VPlexStorageViewInfo storageView = client.getStorageView(vplexClusterName, exportMask.getMaskName());
_log.info("Refreshing ExportMask {}", exportMask.getMaskName());
VPlexControllerUtils.refreshExportMask(_dbClient, storageView, exportMask, targetPortToPwwnMap, _networkDeviceController);
// initiator filter logic is move inside addStepsForRemoveInitiators and addStepsForInitiatorRemoval as
// it is not required for zone related operation.
// validate the remove initiator operation against the export mask volumes
List<URI> volumeURIList = (exportMask.getUserAddedVolumes() != null) ? URIUtil.toURIList(exportMask.getUserAddedVolumes().values()) : new ArrayList<URI>();
if (volumeURIList.isEmpty()) {
_log.warn("volume URI list for validating remove initiators is empty...");
}
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(vplex);
ctx.setExportMask(exportMask);
ctx.setBlockObjects(volumeURIList, _dbClient);
ctx.setAllowExceptions(!WorkflowService.getInstance().isStepInRollbackState(opId));
validator.removeInitiators(ctx).validate();
lastStep = addStepsForRemoveInitiators(vplex, workflow, completer, exportGroup, exportMask, initiators, hostURI, initiatorsAlreadyRemovedFromExportGroup, errorMessages, lastStep);
if (lastStep != null) {
hasStep = true;
}
}
}
String message = errorMessages.toString();
if (isValidationNeeded && !message.isEmpty()) {
_log.error("Error Message {}", errorMessages);
List<String> initiatorNames = new ArrayList<String>();
for (URI initiatorURI : initiatorURIs) {
Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI);
if (initiator != null) {
String normalizedName = Initiator.normalizePort(initiator.getInitiatorPort());
initiatorNames.add(normalizedName);
} else {
_log.warn("no initiator found for URI {}", initiatorURI);
}
}
throw DeviceControllerException.exceptions.removeInitiatorValidationError(Joiner.on(", ").join(initiatorNames), vplex.getLabel(), message);
}
// Fire off the workflow if there were initiators to delete. Otherwise just fire completer.
if (hasStep) {
workflow.executePlan(completer, "Successfully removed initiators: " + initiatorURIs.toString());
} else {
_log.info(String.format("No updates to ExportMasks needed... complete"));
completer.ready(_dbClient);
}
} catch (VPlexApiException vae) {
_log.error("Exception in exportRemoveInitiators: " + initiatorURIs.toString(), vae);
WorkflowStepCompleter.stepFailed(opId, vae);
} catch (Exception ex) {
_log.error("Exception in exportRemoveInitiators: " + initiatorURIs.toString(), ex);
String opName = ResourceOperationTypeEnum.DELETE_EXPORT_INITIATOR.getName();
ServiceError serviceError = VPlexApiException.errors.exportGroupRemoveInitiatorsFailed(opName, ex);
WorkflowStepCompleter.stepFailed(opId, serviceError);
}
}
Aggregations