/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.smis.vnx;
import static com.google.common.collect.Collections2.transform;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import javax.cim.CIMArgument;
import javax.cim.CIMDataType;
import javax.cim.CIMInstance;
import javax.cim.CIMObjectPath;
import javax.cim.CIMProperty;
import javax.cim.UnsignedInteger16;
import javax.wbem.CloseableIterator;
import javax.wbem.WBEMException;
import javax.wbem.client.WBEMClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.emc.storageos.cimadapter.connections.cim.CimConnection;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.AutoTieringPolicy.VnxFastPolicy;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.VirtualPool;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.util.CommonTransformerFunctions;
import com.emc.storageos.db.client.util.CustomQueryUtility;
import com.emc.storageos.db.client.util.StringSetUtil;
import com.emc.storageos.db.client.util.WWNUtility;
import com.emc.storageos.exceptions.DeviceControllerErrors;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.networkcontroller.impl.NetworkDeviceController;
import com.emc.storageos.plugins.common.Constants;
import com.emc.storageos.recoverpoint.utils.WwnUtils;
import com.emc.storageos.recoverpoint.utils.WwnUtils.FORMAT;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.util.ExportUtils;
import com.emc.storageos.util.InvokeTestFailure;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.VolumeURIHLU;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.ExportMaskCreateCompleter;
import com.emc.storageos.volumecontroller.impl.smis.CIMObjectPathFactory;
import com.emc.storageos.volumecontroller.impl.smis.CIMPropertyFactory;
import com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations;
import com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperationsHelper;
import com.emc.storageos.volumecontroller.impl.smis.SmisCommandHelper;
import com.emc.storageos.volumecontroller.impl.smis.SmisConstants;
import com.emc.storageos.volumecontroller.impl.smis.SmisException;
import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils;
import com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext;
import com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext.ExportOperationContextOperation;
import com.emc.storageos.volumecontroller.impl.validators.ValidatorFactory;
import com.emc.storageos.volumecontroller.impl.validators.contexts.ExportMaskValidationContext;
import com.emc.storageos.workflow.WorkflowService;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
public class VnxExportOperations implements ExportMaskOperations {
private static Logger _log = LoggerFactory.getLogger(VnxExportOperations.class);
private SmisCommandHelper _helper;
private DbClient _dbClient;
private CIMObjectPathFactory _cimPath;
private static final int DEFAULT_STORAGE_TIER_METHODOLOGY = 4;
private ValidatorFactory validator;
@Autowired
private NetworkDeviceController _networkDeviceController;
public void setCimObjectPathFactory(CIMObjectPathFactory cimObjectPathFactory) {
_cimPath = cimObjectPathFactory;
}
public void setSmisCommandHelper(SmisCommandHelper helper) {
_helper = helper;
}
public void setDbClient(DbClient dbClient) {
_dbClient = dbClient;
}
public void setValidator(ValidatorFactory validator) {
this.validator = validator;
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations#createExportMask(com.emc.storageos.db.client.
* model.StorageSystem, java.net.URI, com.emc.storageos.volumecontroller.impl.VolumeURIHLU[], java.util.List,
* java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
*
*/
@Override
public void createExportMask(StorageSystem storage,
URI exportMaskURI,
VolumeURIHLU[] volumeURIHLUs,
List<URI> targetURIList,
List<Initiator> initiatorList,
TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} createExportMask START...", storage.getSerialNumber());
try {
_log.info("createExportMask: Export mask id: {}", exportMaskURI);
_log.info("createExportMask: volume-HLU pairs: {}", Joiner.on(',').join(volumeURIHLUs));
_log.info("createExportMask: initiators: {}", Joiner.on(',').join(initiatorList));
_log.info("createExportMask: assignments: {}", Joiner.on(',').join(targetURIList));
ExportOperationContext context = new VnxExportOperationContext();
// Prime the context object
taskCompleter.updateWorkflowStepContext(context);
// https://coprhd.atlassian.net/browse/COP-19019: Validation routine indicates that there
// is some mask- other than the one that we are trying to create -containing the initiators.
// This is an error because initiators can only belong to one VNX StorageGroup. If ViPR performs
// the create, it will succeeded, but just end up moving all the initiators from the existing
// StorageGroup into the new one. This would cause the initiators to lose access to the
// volumes in the original StorageGroup, which is a DU situation. We need to prevent this by
// performing this validation as a precaution.
if (anyInitiatorsAreInAStorageGroup(storage, initiatorList)) {
ServiceError error = SmisException.errors.anExistingSGAlreadyHasTheInitiators(exportMaskURI.toString(),
Joiner.on(',').join(initiatorList));
taskCompleter.error(_dbClient, error);
return;
}
CIMObjectPath[] protocolControllers = createOrGrowStorageGroup(storage,
exportMaskURI, volumeURIHLUs, null, targetURIList, taskCompleter);
if (protocolControllers != null) {
ExportOperationContext.insertContextOperation(taskCompleter, VnxExportOperationContext.OPERATION_CREATE_STORAGE_GROUP,
exportMaskURI);
_log.debug("createExportMask succeeded.");
for (CIMObjectPath protocolController : protocolControllers) {
_helper.setProtocolControllerNativeId(exportMaskURI, protocolController);
}
CimConnection cimConnection = _helper.getConnection(storage);
createOrGrowStorageGroup(storage, exportMaskURI, null, initiatorList, targetURIList, taskCompleter);
// Call populateDeviceNumberFromProtocolControllers only after initiators
// have been added. HLU's will not be reported till the Device is Host
// visible
ExportMaskOperationsHelper.populateDeviceNumberFromProtocolControllers(_dbClient, cimConnection, exportMaskURI,
volumeURIHLUs, protocolControllers, taskCompleter);
modifyClarPrivileges(storage, initiatorList);
taskCompleter.ready(_dbClient);
} else {
_log.debug("createExportMask failed. No protocol controller created.");
ServiceError error = DeviceControllerException.errors.smis.noProtocolControllerCreated();
taskCompleter.error(_dbClient, error);
}
} catch (Exception e) {
_log.error("Unexpected error: createExportMask failed.", e);
ServiceError error = DeviceControllerErrors.smis.methodFailed("createExportMask", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_log.info("{} createExportMask END...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations#deleteExportMask(com.emc.storageos.db.client.
* model.StorageSystem, java.net.URI, java.util.List, java.util.List, java.util.List,
* com.emc.storageos.volumecontroller.TaskCompleter)
*
* IDs
* Note: No need to verify storage ports.
*/
@Override
public void deleteExportMask(StorageSystem storage,
URI exportMaskURI,
List<URI> volumeURIList,
List<URI> targetURIList,
List<Initiator> initiatorList,
TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} deleteExportMask START...", storage.getSerialNumber());
try {
_log.info("Export mask id: {}", exportMaskURI);
if (volumeURIList != null) {
_log.info("deleteExportMask: volumes: {}", Joiner.on(',').join(volumeURIList));
}
if (targetURIList != null) {
_log.info("deleteExportMask: assignments: {}", Joiner.on(',').join(targetURIList));
}
if (initiatorList != null) {
_log.info("deleteExportMask: initiators: {}", Joiner.on(',').join(initiatorList));
}
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
String nativeId = exportMask.getNativeId();
if (Strings.isNullOrEmpty(nativeId)) {
_log.warn(String.format("ExportMask %s does not have a nativeID, " +
"indicating that this export may not have been created " +
"successfully. Marking the delete operation ready.",
exportMaskURI.toString()));
// Perform post-mask-delete cleanup steps
ExportUtils.cleanupAssociatedMaskResources(_dbClient, exportMask);
taskCompleter.ready(_dbClient);
return;
}
boolean isRollback = WorkflowService.getInstance().isStepInRollbackState(taskCompleter.getOpId());
if (isRollback) {
boolean maskCreated = false;
// Get the context from the task completer as this is a rollback.
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance()
.loadStepData(taskCompleter.getOpId());
if (context != null && context.getOperations() != null) {
ListIterator li = context.getOperations().listIterator(context.getOperations().size());
while (li.hasPrevious()) {
ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
_log.info("Handling deleteExportMask as a result of rollback");
if (operation != null
&& VnxExportOperationContext.OPERATION_CREATE_STORAGE_GROUP.equals(operation.getOperation())) {
URI createdExportMaskURI = (URI) operation.getArgs().get(0);
if (exportMask.getId().equals(createdExportMaskURI)) {
maskCreated = true;
break;
}
}
}
}
if (!maskCreated) {
_log.warn(String.format("This is a case of rollback but the ExportMask %s was not found in the export context, " +
"indicating that this export may not have been created successfully. Marking the delete operation ready.",
exportMaskURI.toString()));
// Perform post-mask-delete cleanup steps
ExportUtils.cleanupAssociatedMaskResources(_dbClient, exportMask);
taskCompleter.ready(_dbClient);
return;
}
}
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(storage);
ctx.setExportMask(exportMask);
ctx.setBlockObjects(volumeURIList, _dbClient);
ctx.setInitiators(initiatorList);
ctx.setAllowExceptions(!isRollback);
validator.exportMaskDelete(ctx).validate();
CIMObjectPath protocolController = _cimPath.getClarProtocolControllers(storage, nativeId)[0];
CIMInstance instance = _helper.checkExists(storage, protocolController, true, true);
if (instance != null) {
_helper.setProtocolControllerNativeId(exportMaskURI, null);
ExportMask mask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
if (mask != null) {
List<URI> initiatorURIs = new ArrayList<URI>();
if (mask.getInitiators() != null) {
for (String initUriStr : mask.getInitiators()) {
initiatorURIs.add(URI.create(initUriStr));
}
}
List<Initiator> initiators = _dbClient.queryObject(Initiator.class, initiatorURIs);
deleteStorageHWIDs(storage, initiators);
deleteOrShrinkStorageGroup(storage, exportMaskURI, null, null);
}
}
// Perform post-mask-delete cleanup steps
ExportUtils.cleanupAssociatedMaskResources(_dbClient, exportMask);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_log.error("Unexpected error: deleteExportMask failed.", e);
ServiceError error = DeviceControllerErrors.smis.methodFailed("deleteExportMask", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_log.info("{} deleteExportMask END...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations#addVolume(com.emc.storageos.db.client.model.
* StorageSystem, java.net.URI, com.emc.storageos.volumecontroller.impl.VolumeURIHLU[],
* com.emc.storageos.volumecontroller.TaskCompleter)
*/
@Override
public void addVolumes(StorageSystem storage,
URI exportMaskURI,
VolumeURIHLU[] volumeURIHLUs,
List<Initiator> initiatorList, TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} addVolumes START...", storage.getSerialNumber());
try {
_log.info("addVolumes: Export mask id: {}", exportMaskURI);
_log.info("addVolumes: volume-HLU pairs: {}", Joiner.on(',').join(volumeURIHLUs));
if (initiatorList != null) {
_log.info("addVolumes: initiators impacted: {}", Joiner.on(',').join(initiatorList));
}
ExportOperationContext context = new VnxExportOperationContext();
// Prime the context object
taskCompleter.updateWorkflowStepContext(context);
// Determine if the volume is already in the masking view.
// If so, log and remove from volumes we need to process.
ExportMask mask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
CIMInstance maskingView = _helper.getLunMaskingProtocolController(storage, mask);
Map<String, Integer> discoveredVolumes = new HashMap<String, Integer>();
if (maskingView != null) {
WBEMClient client = _helper.getConnection(storage).getCimClient();
// Get volumes for the masking instance
discoveredVolumes = _helper.getVolumesFromLunMaskingInstance(client, maskingView);
}
List<VolumeURIHLU> removeURIs = new ArrayList<>();
for (VolumeURIHLU volumeUriHLU : volumeURIHLUs) {
BlockObject bo = BlockObject.fetch(_dbClient, volumeUriHLU.getVolumeURI());
if (discoveredVolumes.keySet().contains(bo.getNativeId())) {
_log.info("Found volume {} is already associated with masking view. Assuming this is from a previous operation.",
bo.getLabel());
removeURIs.add(volumeUriHLU);
}
}
// Create the new array of volumes that don't exist yet in the masking view.
VolumeURIHLU[] addVolumeURIHLUs = new VolumeURIHLU[volumeURIHLUs.length - removeURIs.size()];
int index = 0;
for (VolumeURIHLU volumeUriHLU : volumeURIHLUs) {
if (!removeURIs.contains(volumeUriHLU)) {
addVolumeURIHLUs[index++] = volumeUriHLU;
}
}
CIMObjectPath[] protocolControllers = createOrGrowStorageGroup(storage, exportMaskURI, addVolumeURIHLUs, null, null,
taskCompleter);
CimConnection cimConnection = _helper.getConnection(storage);
ExportMaskOperationsHelper.populateDeviceNumberFromProtocolControllers(_dbClient, cimConnection, exportMaskURI,
addVolumeURIHLUs, protocolControllers, taskCompleter);
// Test mechanism to invoke a failure. No-op on production systems.
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_002);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_log.error("Unexpected error: addVolumes failed.", e);
ServiceError error = DeviceControllerErrors.smis.methodFailed("addVolumes", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_log.info("{} addVolumes END...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations#removeVolume(com.emc.storageos.db.client.model.
* StorageSystem, java.net.URI, java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
*
*/
@Override
public void removeVolumes(StorageSystem storage,
URI exportMaskURI,
List<URI> volumeURIList,
List<Initiator> initiatorList,
TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} removeVolumes START...", storage.getSerialNumber());
try {
_log.info("removeVolumes: Export mask id: {}", exportMaskURI);
_log.info("removeVolumes: volumes: {}", Joiner.on(',').join(volumeURIList));
if (initiatorList != null) {
_log.info("removeVolumes: impacted initiators: {}", Joiner.on(",").join(initiatorList));
}
boolean isRollback = WorkflowService.getInstance().isStepInRollbackState(taskCompleter.getOpId());
if (isRollback) {
_log.info("Handling removeVolumes as a result of rollback");
List<URI> addedVolumes = new ArrayList<URI>();
// Get the context from the task completer as this is a rollback.
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance()
.loadStepData(taskCompleter.getOpId());
if (context != null && context.getOperations() != null) {
ListIterator li = context.getOperations().listIterator(context.getOperations().size());
while (li.hasPrevious()) {
ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
if (operation != null
&& VnxExportOperationContext.OPERATION_ADD_VOLUMES_TO_STORAGE_GROUP.equals(operation.getOperation())) {
addedVolumes = (List<URI>) operation.getArgs().get(0);
_log.info("Removing volumes {} as part of rollback", Joiner.on(',').join(addedVolumes));
}
}
}
volumeURIList = addedVolumes;
if (volumeURIList == null || volumeURIList.isEmpty()) {
_log.info("There was no context found for add volumes. So there is nothing to rollback.");
taskCompleter.ready(_dbClient);
return;
}
}
if (null == volumeURIList || volumeURIList.isEmpty()) {
taskCompleter.ready(_dbClient);
_log.warn("{} removeVolumes invoked with zero volumes, resulting in no-op....",
storage.getSerialNumber());
return;
}
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(storage);
ctx.setExportMask(exportMask);
ctx.setInitiators(initiatorList);
ctx.setAllowExceptions(!isRollback);
validator.removeVolumes(ctx).validate();
String[] volumeNames = null;
volumeNames = _helper.getBlockObjectAlternateNames(volumeURIList);
/**
* This extra condition makes sure ViPR do not pass null as volume name while invoking HidePaths.
* If we pass null into HidePaths call, SMI will remove the entire storage group and that will give DU.
*/
if (volumeNames == null || volumeNames.length == 0) {
_log.error("Volume's {} alternate name can not be null", volumeURIList);
ServiceError error = DeviceControllerException.errors.removeVolumeFromMaskFailed(volumeURIList.toString());
taskCompleter.error(_dbClient, error);
} else {
deleteOrShrinkStorageGroup(storage, exportMaskURI, volumeURIList, null);
taskCompleter.ready(_dbClient);
}
} catch (Exception e) {
_log.error("Unexpected error: removeVolumes failed.", e);
ServiceError error = DeviceControllerErrors.smis.methodFailed("removeVolumes", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_log.info("{} removeVolumes END...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations#addInitiator(com.emc.storageos.db.client.model.
* StorageSystem, java.net.URI, java.util.List, java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
*
*/
@Override
public void addInitiators(StorageSystem storage,
URI exportMaskURI,
List<URI> volumeURIs,
List<Initiator> initiatorList,
List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} addInitiators START...", storage.getSerialNumber());
try {
_log.info("addInitiators: Export mask id: {}", exportMaskURI);
if (volumeURIs != null) {
_log.info("addInitiators: volumes : {}", Joiner.on(',').join(volumeURIs));
}
_log.info("addInitiators: initiators : {}", Joiner.on(',').join(initiatorList));
_log.info("addInitiators: targets : {}", Joiner.on(",").join(targets));
ExportOperationContext context = new VnxExportOperationContext();
// Prime the context object
taskCompleter.updateWorkflowStepContext(context);
createOrGrowStorageGroup(storage, exportMaskURI, null, initiatorList, targets, taskCompleter);
// Test mechanism to invoke a failure. No-op on production systems.
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_003);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_log.error("Unexpected error: addInitiators failed.", e);
ServiceError error = DeviceControllerErrors.smis.methodFailed("addInitiators", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_log.info("{} addInitiators END...", storage.getSerialNumber());
}
/*
* (non-Javadoc)
*
* @see
* com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations#removeInitiator(com.emc.storageos.db.client.
* model.StorageSystem, java.net.URI, java.util.List, java.util.List,
* com.emc.storageos.volumecontroller.TaskCompleter)
*
*/
@Override
public void removeInitiators(StorageSystem storage,
URI exportMaskURI,
List<URI> volumeURIList,
List<Initiator> initiatorList,
List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException {
_log.info("{} removeInitiators START...", storage.getSerialNumber());
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 (targets != null) {
_log.info("removeInitiators: targets : {}", Joiner.on(',').join(targets));
}
boolean isRollback = WorkflowService.getInstance().isStepInRollbackState(taskCompleter.getOpId());
if (isRollback) {
_log.info("Handling removeInitiators as a result of rollback");
List<Initiator> addedInitiators = new ArrayList<Initiator>();
// Get the context from the task completer as this is a rollback.
ExportOperationContext context = (ExportOperationContext) WorkflowService.getInstance()
.loadStepData(taskCompleter.getOpId());
if (context != null && context.getOperations() != null) {
ListIterator li = context.getOperations().listIterator(context.getOperations().size());
while (li.hasPrevious()) {
ExportOperationContextOperation operation = (ExportOperationContextOperation) li.previous();
if (operation != null
&& VnxExportOperationContext.OPERATION_ADD_INITIATORS_TO_STORAGE_GROUP.equals(operation.getOperation())) {
addedInitiators = (List<Initiator>) operation.getArgs().get(0);
_log.info("Removing initiators {} as part of rollback", Joiner.on(',').join(addedInitiators));
}
}
}
initiatorList = addedInitiators;
if (initiatorList == null || initiatorList.isEmpty()) {
_log.info("There was no context found for add initiator. So there is nothing to rollback.");
taskCompleter.ready(_dbClient);
return;
}
}
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
ExportMaskValidationContext ctx = new ExportMaskValidationContext();
ctx.setStorage(storage);
ctx.setExportMask(exportMask);
ctx.setBlockObjects(volumeURIList, _dbClient);
ctx.setAllowExceptions(!isRollback);
validator.removeInitiators(ctx).validate();
deleteStorageHWIDs(storage, initiatorList);
taskCompleter.ready(_dbClient);
} catch (Exception e) {
_log.error("Unexpected error: removeInitiators failed.", e);
ServiceError error = DeviceControllerErrors.smis.methodFailed("removeInitiators", e.getMessage());
taskCompleter.error(_dbClient, error);
}
_log.info("{} removeInitiators END...", storage.getSerialNumber());
}
/**
* This call can be used to look up the passed in initiator/port names and find (if
* any) to which export masks they belong on the 'storage' array.
*
*
* @param storage
* [in] - StorageSystem object representing the array
* @param initiatorNames
* [in] - Port identifiers (WWPN or iSCSI name)
* @param mustHaveAllPorts
* [in] NOT APPLICABLE FOR VNX
* @return Map of port name to Set of ExportMask URIs
*/
@Override
public Map<String, Set<URI>> findExportMasks(StorageSystem storage,
List<String> initiatorNames,
boolean mustHaveAllPorts) throws DeviceControllerException {
long startTime = System.currentTimeMillis();
Map<String, Set<URI>> matchingMasks = new HashMap<String, Set<URI>>();
CloseableIterator<CIMInstance> lunMaskingIter = null;
try {
StringBuilder builder = new StringBuilder();
WBEMClient client = _helper.getConnection(storage).getCimClient();
lunMaskingIter = _helper.getClarLunMaskingProtocolControllers(storage);
while (lunMaskingIter.hasNext()) {
CIMInstance instance = lunMaskingIter.next();
String systemName = CIMPropertyFactory.getPropertyValue(instance,
SmisConstants.CP_SYSTEM_NAME);
if (!systemName.contains(storage.getSerialNumber())) {
// We're interested in the specific StorageSystem's masks.
// The above getClarLunMaskingProtocolControllers call will get
// a listing of for all the protocol controllers seen by the
// SMISProvider pointed to by 'storage' system.
continue;
}
String name = CIMPropertyFactory.getPropertyValue(instance,
SmisConstants.CP_ELEMENT_NAME);
CIMProperty<String> deviceIdProperty = (CIMProperty<String>) instance.getObjectPath().getKey(SmisConstants.CP_DEVICE_ID);
// Get volumes and initiators for the masking instance
Map<String, Integer> volumeWWNs = _helper.getVolumesFromLunMaskingInstance(client, instance);
List<String> initiatorPorts = _helper.getInitiatorsFromLunMaskingInstance(client, instance);
// Find out if the port is in this masking container
List<String> matchingInitiators = new ArrayList<String>();
for (String port : initiatorNames) {
String normalizedName = Initiator.normalizePort(port);
if (initiatorPorts.contains(normalizedName)) {
matchingInitiators.add(normalizedName);
}
}
builder.append(String.format("%nXM:%s I:{%s} V:{%s}%n", name,
Joiner.on(',').join(initiatorPorts),
Joiner.on(',').join(volumeWWNs.keySet())));
if (!matchingInitiators.isEmpty()) {
// Look up ExportMask by deviceId/name and storage URI
ExportMask exportMask = ExportMaskUtils.getExportMaskByName(_dbClient, storage.getId(), name);
boolean foundMaskInDb = (exportMask != null);
// If there was no export mask found in the database,
// then create a new one
if (!foundMaskInDb) {
exportMask = new ExportMask();
exportMask.setMaskName(name);
exportMask.setNativeId(deviceIdProperty.getValue());
exportMask.setStorageDevice(storage.getId());
exportMask.setId(URIUtil.createId(ExportMask.class));
exportMask.setCreatedBySystem(false);
// Grab the storage ports that have been allocated for this
// existing mask and add them.
List<String> storagePorts = _helper.getStoragePortsFromLunMaskingInstance(client,
instance);
List<String> storagePortURIs = ExportUtils.storagePortNamesToURIs(_dbClient, storagePorts);
exportMask.setStoragePorts(storagePortURIs);
builder.append(String.format(" ----> SP { %s }\n" +
" URI{ %s }\n",
Joiner.on(',').join(storagePorts),
Joiner.on(',').join(storagePortURIs)));
} else {
// refresh the export mask
refreshExportMask(storage, exportMask);
builder.append('\n');
}
// Update the tracking containers
exportMask.addToExistingVolumesIfAbsent(volumeWWNs);
exportMask.addToExistingInitiatorsIfAbsent(matchingInitiators);
// Update the initiator list to include existing initiators if we know about them.
for (String port : matchingInitiators) {
Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), _dbClient);
if (existingInitiator != null) {
exportMask.addInitiator(existingInitiator);
exportMask.addToUserCreatedInitiators(existingInitiator);
exportMask.removeFromExistingInitiators(existingInitiator);
}
}
// If there were matching initiators, there may be unmatched ones as well that
// belong to the same compute resource. Go through the unmatched initiators and see
// if we have DB entries for them and they belong to the same compute resource. If
// so, add them to the initiator list and remove them from existing as well.
for (String port : initiatorPorts) {
Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), _dbClient);
if (existingInitiator != null && !ExportMaskUtils.checkIfDifferentResource(exportMask, existingInitiator)) {
exportMask.addInitiator(existingInitiator);
exportMask.addToUserCreatedInitiators(existingInitiator);
exportMask.removeFromExistingInitiators(existingInitiator);
}
}
// Update the volume list to include existing volumes if know about them.
if (volumeWWNs != null) {
for (String wwn : volumeWWNs.keySet()) {
URIQueryResultList results = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVolumeWwnConstraint(wwn.toUpperCase()), results);
if (results != null) {
Iterator<URI> resultsIter = results.iterator();
if (resultsIter.hasNext()) {
Volume volume = _dbClient.queryObject(Volume.class, resultsIter.next());
if (volume != null) {
Integer hlu = volumeWWNs.get(wwn);
if (hlu == null) {
_log.warn(String.format(
"The HLU for %s could not be found from the provider. Setting this to -1 (Unknown).",
wwn));
hlu = -1;
}
exportMask.addVolume(volume.getId(), hlu);
exportMask.removeFromExistingVolumes(volume);
}
}
}
}
}
Set existingInitiators = (exportMask.getExistingInitiators() != null) ? exportMask.getExistingInitiators()
: Collections.emptySet();
Set existingVolumes = (exportMask.getExistingVolumes() != null) ? exportMask.getExistingVolumes().keySet()
: Collections.emptySet();
builder.append(String.format("XM:%s is matching. " +
"EI: { %s }, EV: { %s }",
name,
Joiner.on(',').join(existingInitiators),
Joiner.on(',').join(existingVolumes)));
if (foundMaskInDb) {
ExportMaskUtils.sanitizeExportMaskContainers(_dbClient, exportMask);
_dbClient.updateObject(exportMask);
} else {
_dbClient.createObject(exportMask);
}
for (String it : matchingInitiators) {
Set<URI> maskURIs = matchingMasks.get(it);
if (maskURIs == null) {
maskURIs = new HashSet<URI>();
matchingMasks.put(it, maskURIs);
}
maskURIs.add(exportMask.getId());
}
}
}
_log.info(builder.toString());
} catch (Exception e) {
String msg = "Error when attempting to query LUN masking information: " + e.getMessage();
_log.error(MessageFormat.format("Encountered an SMIS error when attempting to query existing exports: {0}", msg), e);
throw SmisException.exceptions.queryExistingMasksFailure(msg, e);
} finally {
if (lunMaskingIter != null) {
lunMaskingIter.close();
}
long totalTime = System.currentTimeMillis() - startTime;
_log.info(String.format("findExportMasks took %f seconds", (double) totalTime / (double) 1000));
}
return matchingMasks;
}
@Override
public Set<Integer> findHLUsForInitiators(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) {
long startTime = System.currentTimeMillis();
Set<Integer> usedHLUs = new HashSet<Integer>();
CloseableIterator<CIMInstance> lunMaskingIter = null;
try {
// Get a mapping of the initiator port names to their CIMObjectPaths on the provider
WBEMClient client = _helper.getConnection(storage).getCimClient();
HashMap<String, CIMObjectPath> initiatorPathsMap = _cimPath.getInitiatorToInitiatorPath(storage, initiatorNames);
List<String> maskNames = new ArrayList<String>();
// Iterate through each initiator port name ...
for (String initiatorName : initiatorPathsMap.keySet()) {
CIMObjectPath initiatorPath = initiatorPathsMap.get(initiatorName);
// Find out if there is a Lun Masking Instance associated with the initiator...
lunMaskingIter = _helper.getAssociatorInstances(storage, initiatorPath, null,
SmisConstants.CLAR_LUN_MASKING_SCSI_PROTOCOL_CONTROLLER, null,
null, SmisConstants.PS_LUN_MASKING_CNTRL_NAME_AND_ROLE);
while (lunMaskingIter.hasNext()) {
// Found a Lun Masking Instance...
CIMInstance instance = lunMaskingIter.next();
String systemName = CIMPropertyFactory.getPropertyValue(instance, SmisConstants.CP_SYSTEM_NAME);
if (!systemName.contains(storage.getSerialNumber())) {
// We're interested in the specific StorageSystem's masks.
// The above getClarLunMaskingProtocolControllers call will get
// a listing of for all the protocol controllers seen by the
// SMISProvider pointed to by 'storage' system.
continue;
}
String name = CIMPropertyFactory.getPropertyValue(instance, SmisConstants.CP_ELEMENT_NAME);
if (!maskNames.contains(name)) {
_log.info("Found matching mask {}", name);
maskNames.add(name);
// Find all the initiators associated with the Masking instance
List<String> initiatorPorts = _helper.getInitiatorsFromLunMaskingInstance(client, instance);
// Get volumes for the Masking instance
Map<String, Integer> volumeWWNs = _helper.getVolumesFromLunMaskingInstance(client, instance);
// add HLUs to set
usedHLUs.addAll(volumeWWNs.values());
_log.info(String.format("%nXM:%s I:{%s} V:{%s} HLU:{%s}%n", name,
Joiner.on(',').join(initiatorPorts),
Joiner.on(',').join(volumeWWNs.keySet()), volumeWWNs.values()));
}
}
}
_log.info(String.format("HLUs found for Initiators { %s }: %s",
Joiner.on(',').join(initiatorNames), usedHLUs));
} catch (Exception e) {
String errMsg = "Encountered an SMIS error when attempting to query used HLUs for initiators: " + e.getMessage();
_log.error(errMsg, e);
throw SmisException.exceptions.hluRetrievalFailed(errMsg, e);
} finally {
if (lunMaskingIter != null) {
lunMaskingIter.close();
}
long totalTime = System.currentTimeMillis() - startTime;
_log.info(String.format("find used HLUs for Initiators took %f seconds", (double) totalTime / (double) 1000));
}
return usedHLUs;
}
@Override
public ExportMask refreshExportMask(StorageSystem storage, ExportMask mask) throws DeviceControllerException {
try {
CIMInstance instance = _helper.getLunMaskingProtocolController(storage, mask);
if (instance != null) {
StringBuilder builder = new StringBuilder();
WBEMClient client = _helper.getConnection(storage).getCimClient();
String name = CIMPropertyFactory.getPropertyValue(instance,
SmisConstants.CP_ELEMENT_NAME);
// Get volumes and initiators for the masking instance
Map<String, Integer> discoveredVolumes = _helper.getVolumesFromLunMaskingInstance(client, instance);
// Update user added volume's HLU information in ExportMask and ExportGroup
ExportMaskUtils.updateHLUsInExportMask(mask, discoveredVolumes, _dbClient);
List<String> discoveredPorts = _helper.getInitiatorsFromLunMaskingInstance(client, instance);
Set existingInitiators = (mask.getExistingInitiators() != null) ? mask.getExistingInitiators() : Collections.emptySet();
Set existingVolumes = (mask.getExistingVolumes() != null) ? mask.getExistingVolumes().keySet() : Collections.emptySet();
builder.append(String.format("%nXM existing objects: %s I{%s} V:{%s}%n", name,
Joiner.on(',').join(existingInitiators),
Joiner.on(',').join(existingVolumes)));
builder.append(String.format("XM discovered: %s I:{%s} V:{%s}%n", name,
Joiner.on(',').join(discoveredPorts),
Joiner.on(',').join(discoveredVolumes.keySet())));
List<String> initiatorsToAddToExisting = new ArrayList<String>();
List<Initiator> initiatorsToAddToUserAddedAndInitiatorList = new ArrayList<Initiator>();
/**
* For the newly discovered initiators, if they are ViPR discovered ports and belong to same resource
* add them to user added and initiators list, otherwise add to existing list.
*/
for (String port : discoveredPorts) {
String normalizedPort = Initiator.normalizePort(port);
if (!mask.hasExistingInitiator(normalizedPort) &&
!mask.hasUserInitiator(normalizedPort)) {
Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), _dbClient);
// Don't add additional initiator to initiators list if it belongs to different host/cluster
if (existingInitiator != null && !ExportMaskUtils.checkIfDifferentResource(mask, existingInitiator)) {
_log.info("Initiator {}->{} belonging to same compute, adding to userAdded and initiator list.",
normalizedPort, existingInitiator.getId());
initiatorsToAddToUserAddedAndInitiatorList.add(existingInitiator);
} else {
initiatorsToAddToExisting.add(normalizedPort);
}
}
}
/**
* Get the existing initiators from the mask and remove the non-discovered ports because
* they are not discovered and are stale.
*
* If the mask has existing initiators but if they are discovered and belongs to same compute resource, then the
* initiators has to get added to user Added and initiators list, and removed from existing list.
*/
List<String> initiatorsToRemoveFromExistingList = new ArrayList<String>();
if (mask.getExistingInitiators() != null &&
!mask.getExistingInitiators().isEmpty()) {
for (String existingInitiatorStr : mask.getExistingInitiators()) {
if (!discoveredPorts.contains(existingInitiatorStr)) {
initiatorsToRemoveFromExistingList.add(existingInitiatorStr);
} else {
Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(existingInitiatorStr),
_dbClient);
if (existingInitiator != null && !ExportMaskUtils.checkIfDifferentResource(mask, existingInitiator)) {
_log.info("Initiator {}->{} belonging to same compute, removing from existing,"
+ " and adding to userAdded and initiator list", existingInitiatorStr, existingInitiator.getId());
initiatorsToAddToUserAddedAndInitiatorList.add(existingInitiator);
initiatorsToRemoveFromExistingList.add(existingInitiatorStr);
}
}
}
}
/**
* Get all the initiators from the mask and remove all the ViPR discovered ports.
* The remaining list has to be removed from user Added and initiator list, because they are not available in ViPR
* but has to be moved to existing list.
*/
List<URI> initiatorsToRemoveFromUserAddedAndInitiatorList = new ArrayList<URI>();
if (mask.getInitiators() != null &&
!mask.getInitiators().isEmpty()) {
initiatorsToRemoveFromUserAddedAndInitiatorList.addAll(transform(mask.getInitiators(),
CommonTransformerFunctions.FCTN_STRING_TO_URI));
for (String port : discoveredPorts) {
String normalizedPort = Initiator.normalizePort(port);
Initiator initiatorDiscoveredInViPR = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), _dbClient);
if (initiatorDiscoveredInViPR != null) {
initiatorsToRemoveFromUserAddedAndInitiatorList.remove(initiatorDiscoveredInViPR.getId());
} else if (!mask.hasExistingInitiator(normalizedPort)) {
_log.info("Initiator {} not found in database, removing from user Added and initiator list,"
+ " and adding to existing list.", port);
initiatorsToAddToExisting.add(normalizedPort);
}
}
}
boolean removeInitiators = !initiatorsToRemoveFromExistingList.isEmpty()
|| !initiatorsToRemoveFromUserAddedAndInitiatorList.isEmpty();
boolean addInitiators = !initiatorsToAddToUserAddedAndInitiatorList.isEmpty()
|| !initiatorsToAddToExisting.isEmpty();
// Check the volumes and update the lists as necessary
Map<String, Integer> volumesToAdd = ExportMaskUtils.diffAndFindNewVolumes(mask, discoveredVolumes);
boolean addVolumes = !volumesToAdd.isEmpty();
boolean removeVolumes = false;
List<String> volumesToRemove = new ArrayList<String>();
if (mask.getExistingVolumes() != null &&
!mask.getExistingVolumes().isEmpty()) {
volumesToRemove.addAll(mask.getExistingVolumes().keySet());
volumesToRemove.removeAll(discoveredVolumes.keySet());
}
// if the volume is in export mask's user added volumes and also in the existing volumes, remove from existing volumes
for (String wwn : discoveredVolumes.keySet()) {
if (mask.hasExistingVolume(wwn)) {
URIQueryResultList volumeList = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory.getVolumeWwnConstraint(wwn), volumeList);
if (volumeList.iterator().hasNext()) {
URI volumeURI = volumeList.iterator().next();
if (mask.hasUserCreatedVolume(volumeURI)) {
builder.append(String.format("\texisting volumes contain wwn %s, but it is also in the "
+ "export mask's user added volumes, so removing from existing volumes", wwn));
volumesToRemove.add(wwn);
}
}
}
}
removeVolumes = !volumesToRemove.isEmpty();
// NOTE/TODO: We are not modifying the storage ports upon refresh like we do for VMAX.
// Refer to CTRL-6982.
builder.append(
String.format("XM refresh: %s existing initiators; add:{%s} remove:{%s}%n",
name, Joiner.on(',').join(initiatorsToAddToExisting),
Joiner.on(',').join(initiatorsToRemoveFromExistingList)));
builder.append(
String.format("XM refresh: %s user added and initiator list; add:{%s} remove:{%s}%n",
name, Joiner.on(',').join(initiatorsToAddToUserAddedAndInitiatorList),
Joiner.on(',').join(initiatorsToRemoveFromUserAddedAndInitiatorList)));
builder.append(
String.format("XM refresh: %s volumes; add:{%s} remove:{%s}%n",
name, Joiner.on(',').join(volumesToAdd.keySet()),
Joiner.on(',').join(volumesToRemove)));
// Any changes indicated, then update the mask and persist it
if (addInitiators || removeInitiators || addVolumes ||
removeVolumes) {
builder.append("XM refresh: There are changes to mask, " +
"updating it...\n");
mask.removeFromExistingInitiators(initiatorsToRemoveFromExistingList);
mask.removeInitiatorURIs(initiatorsToRemoveFromUserAddedAndInitiatorList);
mask.removeFromUserAddedInitiatorsByURI(initiatorsToRemoveFromUserAddedAndInitiatorList);
mask.addInitiators(initiatorsToAddToUserAddedAndInitiatorList);
mask.addToUserCreatedInitiators(initiatorsToAddToUserAddedAndInitiatorList);
mask.addToExistingInitiatorsIfAbsent(initiatorsToAddToExisting);
mask.removeFromExistingVolumes(volumesToRemove);
mask.addToExistingVolumesIfAbsent(volumesToAdd);
// Update the volume list to include existing volumes if know about them.
if (addVolumes) {
for (String wwn : volumesToAdd.keySet()) {
URIQueryResultList results = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVolumeWwnConstraint(wwn.toUpperCase()), results);
if (results != null) {
Iterator<URI> resultsIter = results.iterator();
if (resultsIter.hasNext()) {
Volume volume = _dbClient.queryObject(Volume.class, resultsIter.next());
if (null != volume) {
mask.addVolume(volume.getId(), volumesToAdd.get(wwn));
}
}
}
}
}
ExportMaskUtils.sanitizeExportMaskContainers(_dbClient, mask);
_dbClient.updateObject(mask);
} else {
builder.append("XM refresh: There are no changes to the mask\n");
}
_networkDeviceController.refreshZoningMap(mask,
transform(initiatorsToRemoveFromUserAddedAndInitiatorList, CommonTransformerFunctions.FCTN_URI_TO_STRING),
Collections.<String> emptyList(),
(addInitiators || removeInitiators), true);
_log.info(builder.toString());
}
} catch (Exception e) {
boolean throwException = true;
if (e instanceof WBEMException) {
WBEMException we = (WBEMException) e;
// Only throw exception if code is not CIM_ERROR_NOT_FOUND
throwException = (we.getID() != WBEMException.CIM_ERR_NOT_FOUND);
}
if (throwException) {
String msg = "Error when attempting to query LUN masking information: " + e.getMessage();
_log.error(MessageFormat.format("Encountered an SMIS error when attempting to refresh existing exports: {0}", msg), e);
throw SmisException.exceptions.refreshExistingMaskFailure(msg, e);
}
}
return mask;
}
/**
* This call will attempt to call CreateStorageHardwareID for the given list of
* Initiators. CSHID is called only if the SE_StorageHardwareID (Initiator) is
* not already known to the array. If the Initiator is known to the array, function
* will attempt to extract any known targets end points and return those.
*
* @param storage
* [in] - StorageSystem object representing the array
* @param initiators
* [in] - List Initiator objects
* @return Multimap of Initiator.normalizedPort(initiatorPort) to target endpoint names
* @throws Exception
*/
private Multimap<String, String> createStorageHWIDs(StorageSystem storage, Map<String, CIMObjectPath> existingHwStorageIds,
List<Initiator> initiators, TaskCompleter completer) throws Exception {
_log.info("{} createStorageHWID START...", storage.getSerialNumber());
Multimap<String, String> existingTargets = TreeMultimap.create();
if (initiators == null || initiators.isEmpty()) {
_log.info("No initiators ...");
return existingTargets;
}
try {
CIMObjectPath hwIdManagementSvc = _cimPath.getStorageHardwareIDManagementService(storage);
List<Initiator> createdInitiators = new ArrayList<Initiator>();
for (Initiator initiator : initiators) {
String normalizedPortName = Initiator.normalizePort(initiator.getInitiatorPort());
// Skip any initiators that already exist on the system
if (existingHwStorageIds.containsKey(normalizedPortName)) {
List<String> endpoints = getEMCTargetEndpoints(hwIdManagementSvc, storage,
existingHwStorageIds.get(normalizedPortName));
_log.info("Endpoint found for {} EndPoints {}", normalizedPortName, endpoints);
for (String endpoint : endpoints) {
existingTargets.put(normalizedPortName, endpoint);
_log.info("Endpoint found for {} EndPoint {}", normalizedPortName, endpoint);
}
_log.info("WWNs found on the array already: {}", Joiner.on(',').join(existingHwStorageIds.keySet()));
_log.info(String.format("Initiator %s already exists, skip creation", initiator.getInitiatorPort()));
continue;
}
CIMArgument[] createHwIdIn = _helper.getCreateStorageHardwareIDArgs(initiator);
CIMArgument[] createHwIdOut = new CIMArgument[5];
_helper.invokeMethod(storage, hwIdManagementSvc,
SmisConstants.CREATE_STORAGE_HARDWARE_ID, createHwIdIn, createHwIdOut);
createdInitiators.add(initiator);
}
ExportOperationContext.insertContextOperation(completer,
VnxExportOperationContext.OPERATION_ADD_INITIATORS_TO_STORAGE_GROUP, createdInitiators);
} catch (WBEMException e) {
_log.error("Problem making SMI-S call: ", e);
throw e;
} catch (Exception e) {
_log.error("Unexpected error: createStorageHWIDs failed.", e);
throw e;
}
return existingTargets;
}
/**
* Find any initiator on the storage array that belongs to the same host and grab the "hostname"
* of that initiator. Update our existing transient initiator object so it will use the same name.
*
* @param initiator
* @return
* @throws Exception
*/
private Initiator updateInitiatorBasedOnPeers(StorageSystem storage, Map<String, CIMObjectPath> existingHwStorageIds,
Initiator initiator) throws Exception {
// First, find all initiators for this host
List<Initiator> initiatorsWithHostName = CustomQueryUtility
.queryActiveResourcesByAltId(_dbClient, Initiator.class, "hostname", initiator.getHostName());
CloseableIterator<CIMInstance> seHwIter = null;
try {
if (initiatorsWithHostName != null) {
for (Initiator hostInitiator : initiatorsWithHostName) {
// Exclude any initiator that's not pointing to the same host
// Exclude any initiator that's not pointing to a host at all, such as VPLEX
// Exclude the current initiator because we know we're adding that already.
if (hostInitiator.getId().equals(initiator.getId()) ||
hostInitiator.getHost() == null || initiator.getHost() == null ||
!hostInitiator.getHost().equals(initiator.getHost())) {
continue;
}
// Find this WWN on the storage array
String portName = Initiator.normalizePort(hostInitiator.getInitiatorPort());
if (existingHwStorageIds.containsKey(portName)) {
// Find if this initiator is in any storage group
seHwIter = _helper.getAssociatorInstances(storage, existingHwStorageIds.get(portName), null,
SmisConstants.EMC_CLAR_PRIVILEGE, null, null, SmisConstants.PS_EMC_HOST_NAME);
if (seHwIter != null) {
if (seHwIter.hasNext()) {
CIMInstance priv = seHwIter.next();
String hostName = CIMPropertyFactory.getPropertyValue(priv, SmisConstants.CP_EMC_HOST_NAME);
if (hostName == null || hostName.isEmpty()) {
_log.info("updateInitiatorsBasedOnPeers: could not retrieve hostname of initiator: "
+ hostInitiator.getInitiatorPort());
} else {
// Update the initiator object with this hostname and return.
_log.info("updateInitiatorsBasedOnPeers: retrieved hostname of initiator: "
+ hostInitiator.getInitiatorPort() + " and found hostname " + hostName);
initiator.setHostName(hostName);
return initiator;
}
}
}
}
}
}
} finally {
if (seHwIter != null) {
seHwIter.close();
}
}
// No changes to initiator (default behavior)
return initiator;
}
/**
* Method invokes the SMI-S operation to remove the initiator hardware ID from the
* array. This should be called whenever the initiator is removed from an export or
* when the export is deleted.
*
* @param storage
* [in] - StorageSystem representing the array
* @param initiators
* [in] - An array Initiator objects, whose representation will
* be removed from the array.
* @throws Exception
*/
private void deleteStorageHWIDs(StorageSystem storage,
List<Initiator> initiators) throws Exception {
if (initiators == null || initiators.isEmpty()) {
_log.debug("No initiators ...");
return;
}
CIMObjectPath hwIdManagementSvc = _cimPath
.getStorageHardwareIDManagementService(storage);
for (Initiator initiator : initiators) {
try {
CIMArgument[] createHwIdIn = _helper.getDeleteStorageHardwareIDArgs(storage, initiator);
CIMArgument[] createHwIdOut = new CIMArgument[5];
_helper.invokeMethod(storage, hwIdManagementSvc,
SmisConstants.DELETE_STORAGE_HARDWARE_ID, createHwIdIn,
createHwIdOut);
} catch (WBEMException e) {
_log.error("deleteStorageHWIDs -- WBEMException: " + e.getMessage());
throw e;
} catch (Exception e) {
_log.error("deleteStorageHWIDs -- Exception: " + e.getMessage());
throw e;
}
}
}
/**
* Method invokes the SMI-S operation to modify the initiator parameters such as type and failovermode.
*
* @param storage
* [in] - StorageSystem representing the array
* @param initiators
* [in] - An array Initiator objects, whose representation will
* be removed from the array.
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public void modifyClarPrivileges(StorageSystem storage,
List<Initiator> initiators) throws Exception {
if (initiators == null || initiators.isEmpty()) {
_log.debug("No initiators ...");
return;
}
_log.info("Start -- modifyClarPrivileges");
List<String> initiatorStrings = new ArrayList<String>();
final String RP_INITIATOR_PREFIX = "500124";
final int RP_INITIATOR_TYPE = 31;
final int RP_INITIATOR_FAILOVERMODE = 4;
final CIMProperty[] RP_CLAR_PRIVILIEGE_CIM_PROPERTY = new CIMProperty[] {
new CIMProperty<UnsignedInteger16>(SmisConstants.CP_EMC_INITIATOR_TYPE, CIMDataType.UINT16_T,
new UnsignedInteger16(RP_INITIATOR_TYPE)),
new CIMProperty<UnsignedInteger16>(SmisConstants.CP_EMC_FAILOVER_MODE, CIMDataType.UINT16_T,
new UnsignedInteger16(RP_INITIATOR_FAILOVERMODE))
};
CloseableIterator<CIMInstance> privilegeInstances = null;
for (Initiator initiator : initiators) {
if (initiator.getProtocol().equalsIgnoreCase(Initiator.Protocol.FC.name())) {
initiatorStrings.add(WwnUtils.convertWWN(initiator.getInitiatorNode(), FORMAT.NOMARKERS).toString()
.concat(WwnUtils.convertWWN(initiator.getInitiatorPort(), FORMAT.NOMARKERS).toString()));
}
}
if (initiatorStrings.isEmpty()) {
_log.info("There are no initiators in the list whose privileges need to be changed.");
return;
}
try {
privilegeInstances = _helper.getClarPrivileges(storage);
while (privilegeInstances.hasNext()) {
CIMInstance existingInstance = privilegeInstances.next();
String initiatorType = CIMPropertyFactory.getPropertyValue(existingInstance, SmisConstants.CP_EMC_INITIATOR_TYPE);
// Clar_Privilege consists of instances of all initiators from all the storagesystems that the SMIS is
// connected to. Filter
// for only the ones you need based on the storage system.
// We are only interested in the RP initiators, so check if the initiators are RP initiators
if (existingInstance.toString().contains(storage.getSerialNumber())
&& existingInstance.toString().contains(RP_INITIATOR_PREFIX)) {
for (String initiatorString : initiatorStrings) {
if (existingInstance.toString().contains(initiatorString)
&& (initiatorType != null && Integer.parseInt(initiatorType) != RP_INITIATOR_TYPE)) {
CIMInstance toUpdate = new CIMInstance(existingInstance.getObjectPath(), RP_CLAR_PRIVILIEGE_CIM_PROPERTY);
_log.info("Modifying -- " + existingInstance.toString());
_helper.modifyInstance(storage, toUpdate, SmisConstants.PS_EMC_CLAR_PRIVILEGE);
break;
}
}
}
}
_log.info("end -- modifyClarPrivileges");
} catch (Exception e1) {
_log.error("Unexpected error: modifyClarPrivileges failed");
throw e1;
} finally {
if (null != privilegeInstances) {
privilegeInstances.close();
}
}
}
private CIMObjectPath[] createOrGrowStorageGroup(StorageSystem storage, URI exportMaskURI, VolumeURIHLU[] volumeURIHLUs,
List<Initiator> initiatorList, List<URI> targetURIList, TaskCompleter completer) throws Exception {
// TODO - Refactor createOrGrowStorageGroup by moving code for creating an empty storage group
// to its own createStorageGroup method which calls exposePaths with null for initiators
// and targets
_log.info("{} createOrGrowStorageGroup START...", storage.getSerialNumber());
try {
List<CIMObjectPath> paths = new ArrayList<CIMObjectPath>();
Map<String, CIMObjectPath> existingHwStorageIds = getStorageHardwareIds(storage);
// Determine if the initiator belongs to a host that already has other initiators registered to it.
// If so, we need to register that initiator as the same name as the existing initiators. (CTRL-8407)
if (initiatorList != null) {
for (Initiator initiator : initiatorList) {
updateInitiatorBasedOnPeers(storage, existingHwStorageIds, initiator);
if (initiator != null) {
_log.info("After updateIntiatorBasedOnPeers : {} {}", initiator.getHostName(), initiator.toString());
}
}
}
Multimap<String, String> existingTargets = createStorageHWIDs(storage, existingHwStorageIds, initiatorList, completer);
if (initiatorList != null && existingTargets.keySet().size() == initiatorList.size()) {
_log.info(String.format("All the initiators are known to the array and have target endpoints: %s\n." +
"These are the targets %s",
Joiner.on(',').join(existingTargets.entries()),
Joiner.on(',').join(targetURIList)));
}
Multimap<URI, Initiator> targetPortsToInitiators = HashMultimap.create();
// Some of the Initiators are already registered partially on the array based on pre existing zoning
// COP-16954 We need to manually register them, the Initiators will have HardwareId created but,
// The registration is not complete.. createHardwareIDs method above will include those Initiators
_log.info("Preregistered Target and Initiator ports processing .. Start");
// Map to hash translations
HashMap<String, URI> targetPortMap = new HashMap<>();
for (String initPort : existingTargets.keySet()) {
_log.info("InitiatorPort {} and TargetStoragePort {}", initPort, existingTargets.get(initPort));
// IntiatorPort 50012481006B7807 and TargetStoragePort
// [CLARIION+CKM00115001014+PORT+50:06:01:60:3E:A0:45:79,
// CLARIION+CKM00115001014+PORT+50:06:01:61:3E:A0:45:79]
if (!WWNUtility.isValidNoColonWWN(initPort)) {
_log.info("InitiatorPort {} is not a valid FC WWN so ignore it", initPort);
continue;
}
Collection<String> targetPorts = existingTargets.get(initPort);
for (String targetPortGuid : targetPorts) {
URI targetPortURI = targetPortMap.get(targetPortGuid);
if (targetPortURI == null) {
targetPortURI = getStoragePortURI(targetPortGuid);
targetPortMap.put(targetPortGuid, targetPortURI);
}
Initiator translatedInitiator = getInitiatorForWWN(initPort);
_log.info("Calculating Initiator {} and Target {}", translatedInitiator, targetPortURI);
if (targetPortURI != null && translatedInitiator != null) {
targetPortsToInitiators.put(targetPortURI, translatedInitiator);
} else {
_log.info("Initiator WWN {} translation was null or targetPort is null {}",
initPort, targetPortURI);
}
}
}
_log.info("Preregistered Target and Initiator ports processing .. End");
List<URI> volumeURIs = new ArrayList<URI>();
if (volumeURIHLUs != null && volumeURIHLUs.length > 0) {
for (VolumeURIHLU volumeURIHLU : volumeURIHLUs) {
volumeURIs.add(volumeURIHLU.getVolumeURI());
}
}
if (initiatorList == null || initiatorList.isEmpty()) {
_log.info("InitiatorList is null or Empty so call exposePathsWithVolumesOnly");
paths.addAll(Arrays.asList(exposePathsWithVolumesOnly(storage, exportMaskURI, volumeURIHLUs)));
} else {
ExportMask mask = _dbClient.queryObject(ExportMask.class, exportMaskURI);
for (Initiator initiator : initiatorList) {
// TODO - Ask Tom is there is a reason why we should not do this instead of old code
List<URI> tzTargets = ExportUtils.getInitiatorPortsInMask(mask, initiator, _dbClient);
_log.info("Calculating Intiator {} and Targets {}", initiator, tzTargets);
if (!tzTargets.isEmpty()) {
for (URI targetURI : tzTargets) {
targetPortsToInitiators.put(targetURI, initiator);
}
}
}
_log.info("Call manuallyRegisterHostInitiators with {} ", targetPortsToInitiators.toString());
// Register the initiator to target port mappings
manuallyRegisterHostInitiators(storage, targetPortsToInitiators);
// CTRL-9086
// Modify the list of initiators list to match what is being mapped. If there are any initiators
// that are passed to the ExposePaths call that weren't manuallyRegistered (above), then those
// initiators will automatically get mapped all the array's StoragePorts.
//
// If the targetPortsToInitiators MultiMap is empty, then we will send all the initiators.
// Presumably, in this situation there are already some existing mappings for the initiators,
// so would just need to call ExposePaths with those initiators, so that they get added to the
// StorageGroup
List<Initiator> initiatorsToExpose = initiatorList;
if (!targetPortsToInitiators.isEmpty()) {
Map<URI, Initiator> uniqueInitiatorMap = new HashMap<>();
for (Collection<Initiator> initiatorCollection : targetPortsToInitiators.asMap().values()) {
for (Initiator initiator : initiatorCollection) {
uniqueInitiatorMap.put(initiator.getId(), initiator);
}
}
initiatorsToExpose = new ArrayList<>(uniqueInitiatorMap.values());
// CTRL-10022
// If the exportMask needs to use only a subset of initiators, then we should
// adjust its initiator list to match. Otherwise, the masking orchestrator will get
// confused about the initiators.
if (completer instanceof ExportMaskCreateCompleter) {
ExportMaskCreateCompleter createCompleter = ((ExportMaskCreateCompleter) completer);
List<URI> removedInitiators = new ArrayList<>();
List<URI> maskInitiators = StringSetUtil.stringSetToUriList(mask.getInitiators());
for (URI maskInitiator : maskInitiators) {
if (!uniqueInitiatorMap.containsKey(maskInitiator)) {
mask.removeInitiator(maskInitiator);
removedInitiators.add(maskInitiator);
}
}
_dbClient.updateObject(mask);
if (!removedInitiators.isEmpty()) {
_log.info(String.format("The following initiators will not be mapped, hence they will be " +
"removed from the initiator list of ExportMask %s (%s): %s",
mask.getMaskName(), mask.getId(), Joiner.on(',').join(removedInitiators)));
}
// Adjust the completer's initiator list
createCompleter.removeInitiators(removedInitiators);
}
}
_log.info(String.format("ExposePaths will be called with these initiators: %s",
Joiner.on(',').join(Collections2.transform(initiatorsToExpose,
CommonTransformerFunctions.fctnInitiatorToPortName()))));
// Add all the initiators to the StorageGroup
paths.addAll(Arrays.asList(exposePathsWithVolumesAndInitiatorsOnly(storage, exportMaskURI, volumeURIHLUs,
initiatorsToExpose)));
}
ExportOperationContext.insertContextOperation(completer,
VnxExportOperationContext.OPERATION_ADD_VOLUMES_TO_STORAGE_GROUP, volumeURIs);
_log.info("{} createOrGrowStorageGroup END...", storage.getSerialNumber());
return paths.toArray(new CIMObjectPath[paths.size()]);
} catch (WBEMException e) {
_log.error("Problem making SMI-S call: ", e);
throw e;
} catch (Exception e) {
_log.error("Unexpected error: createOrGrowStorageGroup failed.", e);
throw e;
}
}
private CIMObjectPath[] deleteOrShrinkStorageGroup(StorageSystem storage,
URI exportMaskURI,
List<URI> volumeURIList,
List<Initiator> initiatorList) throws Exception {
_log.debug("{} deleteOrShrinkStorageGroup START...", storage.getSerialNumber());
try {
boolean bDeleteStorageGroup = (volumeURIList == null && initiatorList == null);
CIMArgument[] inArgs = _helper.getDeleteOrShrinkStorageGroupInputArguments(storage,
exportMaskURI, volumeURIList, initiatorList, bDeleteStorageGroup);
CIMArgument[] outArgs = new CIMArgument[5];
if (bDeleteStorageGroup) {
_helper.invokeMethod(storage, _cimPath.getControllerConfigSvcPath(storage),
"DeleteProtocolController", inArgs, outArgs);
} else {
_helper.invokeMethod(storage, _cimPath.getControllerConfigSvcPath(storage),
"HidePaths", inArgs, outArgs);
}
_log.debug("{} deleteOrShrinkStorageGroup END...", storage.getSerialNumber());
return _cimPath.getProtocolControllersFromOutputArgs(outArgs);
} catch (WBEMException e) {
_log.error("Problem making SMI-S call: ", e);
throw e;
} catch (Exception e) {
_log.error("Unexpected error: deleteOrShrinkStorageGroup failed.", e);
throw e;
}
}
/**
* Routine will return a list of SE_StorageHardwareID.ElementName values from the
* provider managing the specified 'storage' array.
*
* The values will be normalized. That is, in the case of WWN,
* it will be all uppercase with colons (if any) removed.
*
* @param storage
* [in] - StorageSystem object
* @return Map of String(initiator port name) to CIMObjectPath representing the Initiator in SMI-S
* @throws Exception
*/
private Map<String, CIMObjectPath> getStorageHardwareIds(StorageSystem storage) throws Exception {
Map<String, CIMObjectPath> idsMap = new HashMap<>();
CloseableIterator<CIMInstance> seHwIter = null;
try {
// Multiple arrays can be managed by a single SMI-S instance. The SE_StorageHardwareID is
// global to the provider, so we need to get the SE_StorageHardware_ID object that are
// associated with a specific array.
CIMObjectPath hwManagementIDSvcPath = _cimPath.getStorageHardwareIDManagementService(storage);
seHwIter = _helper.getAssociatorInstances(storage, hwManagementIDSvcPath, null,
SmisConstants.CP_SE_STORAGE_HARDWARE_ID, null, null, SmisConstants.PS_ELEMENT_NAME);
while (seHwIter.hasNext()) {
CIMInstance instance = seHwIter.next();
String port = CIMPropertyFactory.getPropertyValue(instance,
SmisConstants.CP_ELEMENT_NAME);
String elementName = Initiator.normalizePort(port);
idsMap.put(elementName, instance.getObjectPath());
}
} finally {
if (seHwIter != null) {
seHwIter.close();
}
}
return idsMap;
}
/**
* Looks up the targets that are associated with the initiator (if any).
*
* @param idMgmtSvcPath
* [in] - Clar_StorageHardwareIDManagementService CIMObjectPath
* @param storage
* [in] - StorageSystem object representing the array
* @param initiator
* [in] - CIMObjectPath representing initiator to lookup target endpoints (StoragePorts) for
* @return List or StoragePort URIs that were found to be end points for the initiator
* @throws Exception
*/
private List<String> getEMCTargetEndpoints(CIMObjectPath idMgmtSvcPath, StorageSystem storage,
CIMObjectPath initiator) throws Exception {
List<String> endpoints = new ArrayList<>();
try {
CIMArgument[] input = _helper.getEMCGetConnectedTargetEndpoints(initiator);
CIMArgument[] output = new CIMArgument[5];
_helper.invokeMethod(storage, idMgmtSvcPath, SmisConstants.EMC_GET_TARGET_ENDPOINTS, input, output);
CIMObjectPath[] tePaths = (CIMObjectPath[]) _cimPath.getFromOutputArgs(output, SmisConstants.CP_TARGET_ENDPOINTS);
if (tePaths != null) {
for (CIMObjectPath tePath : tePaths) {
CIMInstance teInstance = _helper.getInstance(storage, tePath, false, false, SmisConstants.PS_NAME);
String tePortNetworkId = CIMPropertyFactory.getPropertyValue(teInstance, SmisConstants.CP_NAME);
List<StoragePort> storagePorts = CustomQueryUtility.queryActiveResourcesByAltId(_dbClient, StoragePort.class,
"portNetworkId",
WWNUtility.getWWNWithColons(tePortNetworkId));
for (StoragePort port : storagePorts) {
endpoints.add(port.getNativeGuid());
}
}
}
_log.info(String.format("Initiator %s has these target endpoints: [ %s ]", initiator.toString(),
Joiner.on(',').join(endpoints)));
} catch (WBEMException e) {
// The initiator CIMObjectPath passed into this function was determined by getting
// the associators to the StorageHardwareIDManagementService. When we call
// getEMCTargetEndpoints, it is done based on seeing that the initiator is in this
// associator list. Sometimes, the provider is returing initiator CIMObjectPaths
// that actually do not exist on the array. In this case, there will be WBEMException
// thrown when we try to get the targets storage ports using this CIMObject reference.
// So, here we're trying to protect against this possibility.
_log.info(String.format("Could not get TargetEndPoints for %s - %s", initiator, e.getMessage()));
}
return endpoints;
}
/**
* Method to call EMCManuallyRegisterHostInitiators. This call will bind initiators and target ports
* on the VNX array.
*
* @param storage
* [in] - StorageSystem object
* @param targetPortsToInitiators
* [in] - Multimap that holds a reference of storage port URI to a list
* of Initiator objects. These will be used for the SMI-S calls.
* @throws Exception
*/
private void manuallyRegisterHostInitiators(StorageSystem storage,
Multimap<URI, Initiator> targetPortsToInitiators)
throws Exception {
_log.info("manuallyRegisterHostInitiators Start : {}", targetPortsToInitiators);
for (Map.Entry<URI, Collection<Initiator>> t2is : targetPortsToInitiators.asMap().entrySet()) {
URI storagePortURI = t2is.getKey();
Collection<Initiator> initiators = t2is.getValue();
_log.info("Manually register : Initiators {}. StoragePort {}", initiators, storagePortURI);
CIMArgument[] inArgs = _helper.getEMCManuallyRegisterHostInitiators(storage, initiators, storagePortURI);
CIMArgument[] outArgs = new CIMArgument[5];
_helper.invokeMethod(storage, _cimPath.getStorageHardwareIDManagementService(storage),
SmisConstants.EMC_MANUALLY_REGISTER_HOST_INITIATORS, inArgs, outArgs);
}
_log.info("manuallyRegisterHostInitiators End : ");
}
/**
* Wrapper function of exposePaths. This one only using the volumes to call exposePaths.
*
* @param storage
* [in] - StorageSystem object representing the array
* @param exportMaskURI
* [in] - ExportMask URI reference
* @param volumeURIHLUs
* [in] - Array representing VolumeURIs to HLUs
* @return An array CIMObjectPaths representing the ProtocolController that was created/updated.
* @throws Exception
*/
private CIMObjectPath[] exposePathsWithVolumesOnly(StorageSystem storage,
URI exportMaskURI,
VolumeURIHLU[] volumeURIHLUs) throws Exception {
CIMArgument[] inArgs = _helper.getCreateOrGrowStorageGroupInputArguments(storage,
exportMaskURI, volumeURIHLUs, null, null);
CIMArgument[] outArgs = new CIMArgument[5];
_helper.invokeMethod(storage, _cimPath.getControllerConfigSvcPath(storage),
"ExposePaths", inArgs, outArgs);
return _cimPath.getProtocolControllersFromOutputArgs(outArgs);
}
/**
* Wrapper function of exposePaths. This one only using the volumes and initiators to call exposePaths.
*
* @param storage
* [in] - StorageSystem object representing the array
* @param exportMaskURI
* [in] - ExportMask URI reference
* @param volumeURIHLUs
* [in] - Array representing VolumeURIs to HLUs
* @param initiatorList
* [in] - List of Initiator objects
* @return An array CIMObjectPaths representing the ProtocolController that was created/updated.
* @throws Exception
*/
private CIMObjectPath[] exposePathsWithVolumesAndInitiatorsOnly(StorageSystem storage,
URI exportMaskURI,
VolumeURIHLU[] volumeURIHLUs,
List<Initiator> initiatorList) throws Exception {
CIMArgument[] inArgs = _helper.getCreateOrGrowStorageGroupInputArguments(storage,
exportMaskURI, volumeURIHLUs, initiatorList, null);
CIMArgument[] outArgs = new CIMArgument[5];
_helper.invokeMethod(storage, _cimPath.getControllerConfigSvcPath(storage),
"ExposePaths", inArgs, outArgs);
return _cimPath.getProtocolControllersFromOutputArgs(outArgs);
}
/**
* Updates Auto-tiering policy for the given volumes.
*
* @param storage
* the storage
* @param exportMask
* the export mask
* @param volumeURIs
* the volume uris
* @param newVirtualPool
* the new virtual pool where policy name can be obtained
* @param rollback
* boolean to know if it is called as a roll back step from workflow.
* @param taskCompleter
* @throws Exception
* the exception
*/
@Override
public void updateStorageGroupPolicyAndLimits(StorageSystem storage, ExportMask exportMask,
List<URI> volumeURIs, VirtualPool newVirtualPool, boolean rollback,
TaskCompleter taskCompleter) throws Exception {
String message = rollback ? ("updateAutoTieringPolicy" + "(rollback)") : ("updateAutoTieringPolicy");
_log.info("{} {} START...", storage.getSerialNumber(), message);
_log.info("{} : volumeURIs: {}", message, volumeURIs);
try {
String newPolicyName = ControllerUtils.getFastPolicyNameFromVirtualPool(_dbClient, storage, newVirtualPool);
_log.info("{} : AutoTieringPolicy: {}", message, newPolicyName);
List<Volume> volumes = _dbClient.queryObject(Volume.class, volumeURIs);
/**
* get tier methodology for policy name
* volume has tier methodology as '4' when no policy set (START_HIGH_THEN_AUTO_TIER).
*
* For VNX, Policy is set on Volumes during creation.
*/
int storageTierMethodologyId = DEFAULT_STORAGE_TIER_METHODOLOGY;
if (!Constants.NONE.equalsIgnoreCase(newPolicyName)) {
storageTierMethodologyId = getStorageTierMethodologyFromPolicyName(newPolicyName);
}
// Build list of native ids
Set<String> nativeIds = new HashSet<String>();
for (Volume volume : volumes) {
nativeIds.add(volume.getNativeId());
}
_log.info("Native Ids of Volumes: {}", nativeIds);
CimConnection connection = _helper.getConnection(storage);
WBEMClient client = connection.getCimClient();
// CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage);
String[] memberNames = nativeIds.toArray(new String[nativeIds.size()]);
CIMObjectPath[] volumePaths = _cimPath.getVolumePaths(storage,
memberNames);
CIMProperty[] inArgs = _helper
.getModifyStorageTierMethodologyIdInputArguments(storageTierMethodologyId);
for (CIMObjectPath volumeObject : volumePaths) {
if (_helper.getVolumeStorageTierMethodologyId(storage, volumeObject) == storageTierMethodologyId) {
_log.info(
"Current and new Storage Tier Methodology Ids are same '{}'." +
" No need to update it on Volume Object Path {}.",
storageTierMethodologyId, volumeObject);
} else {
CIMInstance modifiedSettingInstance = new CIMInstance(volumeObject,
inArgs);
_log.info(
"Updating Storage Tier Methodology ({}) on Volume Object Path {}.",
storageTierMethodologyId, volumeObject);
client.modifyInstance(modifiedSettingInstance,
SmisConstants.PS_EMC_STORAGE_TIER_METHODOLOGY);
}
}
taskCompleter.ready(_dbClient);
} catch (Exception e) {
String errMsg = String
.format("An error occurred while updating Auto-tiering policy for Volumes %s",
volumeURIs);
_log.error(errMsg, e);
ServiceError serviceError = DeviceControllerException.errors
.jobFailedMsg(errMsg, e);
taskCompleter.error(_dbClient, serviceError);
}
_log.info("{} {} END...", storage.getSerialNumber(), message);
}
/**
* Gets the storage tier methodology from policy name.
*
* @param policyName
* the policy name
* @return the storage tier methodology from policy name
*/
private int getStorageTierMethodologyFromPolicyName(String policyName) {
int storageTierMethodologyId = 0;
switch (VnxFastPolicy.valueOf(policyName)) {
case DEFAULT_NO_MOVEMENT:
storageTierMethodologyId = Constants.NO_DATA_MOVEMENT;
break;
case DEFAULT_AUTOTIER:
storageTierMethodologyId = Constants.AUTO_TIER;
break;
case DEFAULT_HIGHEST_AVAILABLE:
storageTierMethodologyId = Constants.HIGH_AVAILABLE_TIER;
break;
case DEFAULT_LOWEST_AVAILABLE:
storageTierMethodologyId = Constants.LOW_AVAILABLE_TIER;
break;
case DEFAULT_START_HIGH_THEN_AUTOTIER:
storageTierMethodologyId = Constants.START_HIGH_THEN_AUTO_TIER;
break;
default:
// volume has tier methodology as '4' when no policy set.
// START_HIGH_THEN_AUTO_TIER is the default and recommended policy
storageTierMethodologyId = Constants.START_HIGH_THEN_AUTO_TIER;
break;
}
return storageTierMethodologyId;
}
@Override
public Map<URI, Integer> getExportMaskHLUs(StorageSystem storage, ExportMask exportMask) {
Map<URI, Integer> hlus = Collections.emptyMap();
try {
CIMInstance instance = _helper.getLunMaskingProtocolController(storage, exportMask);
// There's a StorageGroup on the array for the ExportMask and it has userAddedVolumes.
if (instance != null && exportMask.getUserAddedVolumes() != null) {
hlus = new HashMap<>();
WBEMClient client = _helper.getConnection(storage).getCimClient();
// Get the volume WWN to HLU mapping from the StorageGroup
Map<String, Integer> discoveredVolumes = _helper.getVolumesFromLunMaskingInstance(client, instance);
for (String wwn : discoveredVolumes.keySet()) {
Integer hlu = discoveredVolumes.get(wwn);
if (hlu != null && exportMask.getUserAddedVolumes().containsKey(wwn)) {
// Look up the volume URI given the WWN
String uriString = exportMask.getUserAddedVolumes().get(wwn);
// We have a proper HLU
hlus.put(URI.create(uriString), hlu);
}
}
}
_log.info(String.format("Retrieved these volumes from ExportMask %s (%s): %s", exportMask.getMaskName(), exportMask.getId(),
CommonTransformerFunctions.collectionString(hlus.entrySet())));
} catch (Exception e) {
// Log an error, but return an empty list
_log.error(String.format("Encountered an exception when attempting to get volume to HLU mapping from ExportMask %s",
exportMask.getMaskName()), e);
// We encountered an exception, so let's not return partial data ...
if (!hlus.isEmpty()) {
hlus.clear();
}
}
return hlus;
}
/**
* Gets the Storage Port(s) associated with the GUID passed
* Returns empty list if no storage ports found
*/
private URI getStoragePortURI(String storagePortGuid) {
URIQueryResultList uriQueryList = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getStoragePortByNativeGuidConstraint(storagePortGuid), uriQueryList);
while (uriQueryList.iterator().hasNext()) {
URI uri = uriQueryList.iterator().next();
StoragePort storagePort = _dbClient.queryObject(StoragePort.class, uri);
if (storagePort != null && !storagePort.getInactive()) {
_log.info("getStoagePortURI called with {} and result {}", storagePortGuid, uri);
return uri;
}
}
return null;
}
/**
* Gets the Initiator Port associated with the WWN passed
* Returns null if no Initiators are found
*/
private Initiator getInitiatorForWWN(String WWN) {
String formatedWWN = WWNUtility.getWWNWithColons(WWN);
Initiator init = ExportUtils.getInitiator(formatedWWN, _dbClient);
_log.info("getInitiatorForWWN called with {} and result {}", WWN + ":" + formatedWWN, init);
return init;
}
/**
* Returns true if one or all of the 'initiators' are associated to an active VNX StorageGroup on 'storage'.
*
* @param storage
* [IN] - StorageSystem representing the VNX array to check
* @param initiators
* [IN] - Initiators to check for association to existing ExportMask(s)
* @return true iff any of the initiators were found to be associated with some ExportMask on the array.
*/
private boolean anyInitiatorsAreInAStorageGroup(StorageSystem storage, List<Initiator> initiators) {
List<String> portNames = new ArrayList<>(Collections2.transform(initiators, CommonTransformerFunctions.fctnInitiatorToPortName()));
Map<String, Set<URI>> foundMasks = findExportMasks(storage, portNames, false);
// Return true when there was a match found (i.e., when foundMasks is not empty)
return !foundMasks.isEmpty();
}
@Override
public void addPaths(StorageSystem storage, URI exportMask, Map<URI, List<URI>> newPaths, TaskCompleter taskCompleter)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void removePaths(StorageSystem storage, URI exportMask, Map<URI, List<URI>> adjustedPaths, Map<URI, List<URI>> removePaths, TaskCompleter taskCompleter)
throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
}