/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.hds.prov;
import java.net.URI;
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.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import com.emc.storageos.customconfigcontroller.CustomConfigConstants;
import com.emc.storageos.customconfigcontroller.DataSource;
import com.emc.storageos.customconfigcontroller.DataSourceFactory;
import com.emc.storageos.customconfigcontroller.impl.CustomConfigHandler;
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.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.AutoTieringPolicy.HitachiTieringPolicy;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.HostInterface;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageProtocol.Transport;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.StringSetMap;
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.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.hds.HDSConstants;
import com.emc.storageos.hds.HDSException;
import com.emc.storageos.hds.api.HDSApiClient;
import com.emc.storageos.hds.api.HDSApiExportManager;
import com.emc.storageos.hds.api.HDSApiFactory;
import com.emc.storageos.hds.model.FreeLun;
import com.emc.storageos.hds.model.HDSHost;
import com.emc.storageos.hds.model.HostStorageDomain;
import com.emc.storageos.hds.model.ISCSIName;
import com.emc.storageos.hds.model.LDEV;
import com.emc.storageos.hds.model.LogicalUnit;
import com.emc.storageos.hds.model.Path;
import com.emc.storageos.hds.model.WorldWideName;
import com.emc.storageos.model.ResourceOperationTypeEnum;
import com.emc.storageos.networkcontroller.impl.NetworkDeviceController;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.util.ExportUtils;
import com.emc.storageos.util.NetworkLite;
import com.emc.storageos.util.NetworkUtil;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.VolumeURIHLU;
import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSJob;
import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSModifyVolumeJob;
import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSUtils;
import com.emc.storageos.volumecontroller.impl.job.QueueJob;
import com.emc.storageos.volumecontroller.impl.smis.ExportMaskOperations;
import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.gson.internal.Pair;
/**
* This class is responsible to handle all export usecases like
* 1. Creating an ExportMask
* 2. Deleting an ExportMask
* 3. Add volume to an ExportMask
* 4. Delete Volume from an ExportMask
* 5. Add Initiators to ExportMask
* 6. Delete Initiators from ExportMask
* 7. Refresh ExportMask
*
*/
public class HDSExportOperations implements ExportMaskOperations {
private static Logger log = LoggerFactory.getLogger(HDSExportOperations.class);
private DbClient dbClient;
private HDSApiFactory hdsApiFactory;
private static final List<String> hostModeSupportedModels = new ArrayList<String>();
@Autowired
private DataSourceFactory dataSourceFactory;
@Autowired
private CustomConfigHandler customConfigHandler;
@Autowired
private NetworkDeviceController _networkDeviceController;
static {
// When a new model supports host modes then we should add them to this list.
hostModeSupportedModels.add(HDSConstants.HUSVM_ARRAYFAMILY_MODEL);
hostModeSupportedModels.add(HDSConstants.VSP_ARRAYFAMILY_MODEL);
hostModeSupportedModels.add(HDSConstants.USPV_ARRAYFAMILY_MODEL);
hostModeSupportedModels.add(HDSConstants.USP_ARRAYFAMILY_MODEL);
hostModeSupportedModels.add(HDSConstants.VSP_G1000_ARRAYFAMILY_MODEL);
}
public static enum HitachiHostMode {
Windows("Windows Extension"), Linux("Standard"), Solaris("Solaris"), HPUX("HP"), Esx("VMware Extension"), AIX("AIX"), Netware(
"Netware");
private String _key;
private static HitachiHostMode[] copyOfValues = values();
HitachiHostMode(String key) {
_key = key;
}
public String getKey() {
return _key;
}
public static String getHostMode(String id) {
for (HitachiHostMode type : copyOfValues) {
if (type.name().equalsIgnoreCase(id)) {
return type.getKey();
}
}
return null;
}
public static String getHostModeName(String key) {
for (HitachiHostMode policyType : copyOfValues) {
if (policyType.getKey().equalsIgnoreCase(key)) {
return policyType.name();
}
}
return null;
}
}
public void setDbClient(DbClient dbClient) {
this.dbClient = dbClient;
}
/**
* @param hdsApiFactory
* the hdsApiFactory to set
*/
public void setHdsApiFactory(HDSApiFactory hdsApiFactory) {
this.hdsApiFactory = hdsApiFactory;
}
/**
* Creates a ExportMask with the given initiators & volumes.
*
* Below are the steps to follow to create an Export Mask on Hitachi array.
* Step 1: Register host with initiators.
* Step 2: Based on the targetport type, create a Host Storage Domain.
* Step 3: Add WWN/ISCSI names to the Host Storage Domain.
* Step 4: Add Luns to the HostStorageDomain created in step 2.
*
*/
@Override
public void createExportMask(StorageSystem storage, URI exportMaskId,
VolumeURIHLU[] volumeURIHLUs, List<URI> targetURIList,
List<Initiator> initiatorList, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} createExportMask START...", storage.getSerialNumber());
HDSApiClient hdsApiClient = null;
String systemObjectID = null;
ExportMask exportMask = null;
List<HostStorageDomain> hsdsWithInitiators = null;
List<HostStorageDomain> hsdsToCreate = null;
try {
log.info("createExportMask: Export mask id :{}", exportMaskId);
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));
hdsApiClient = hdsApiFactory.getClient(getHDSServerManagementServerInfo(storage),
storage.getSmisUserName(), storage.getSmisPassword());
systemObjectID = HDSUtils.getSystemObjectID(storage);
exportMask = dbClient.queryObject(ExportMask.class, exportMaskId);
// Check whether host is already registered or not. If host is not
// registered, add the host.
registerHostsWithInitiators(initiatorList, hdsApiClient);
List<StoragePort> storagePorts = dbClient.queryObject(StoragePort.class, targetURIList, true);
if (checkIfMixedTargetPortTypeSelected(storagePorts)) {
log.error("Unsupported Host as it has both FC & iSCSI Initiators");
throw HDSException.exceptions.unsupportedConfigurationFoundInHost();
}
if (null != targetURIList && !targetURIList.isEmpty()) {
String hostName = getHostNameForInitiators(initiatorList);
String hostMode = null, hostModeOption = null;
Pair<String, String> hostModeInfo = getHostModeInfo(storage, initiatorList);
if (hostModeInfo != null) {
hostMode = hostModeInfo.first;
hostModeOption = hostModeInfo.second;
}
hsdsToCreate = processTargetPortsToFormHSDs(hdsApiClient, storage,
targetURIList, hostName, exportMask, hostModeInfo, systemObjectID);
// Step 1: Create all HSD's using batch operation.
List<HostStorageDomain> hsdResponseList = hdsApiClient
.getHDSBatchApiExportManager().addHostStorageDomains(
systemObjectID, hsdsToCreate, storage.getModel());
if (null == hsdResponseList || hsdResponseList.isEmpty()) {
log.error("Batch HSD creation failed. Aborting operation...");
throw HDSException.exceptions.notAbleToAddHSD(storage
.getSerialNumber());
}
// Step 2: Add initiators to all HSD's.
hsdsWithInitiators = executeBatchHSDAddInitiatorsCommand(hdsApiClient,
systemObjectID, hsdResponseList, storagePorts, initiatorList, storage.getModel());
// Step 3: Add volumes to all HSD's.
List<Path> allHSDPaths = executeBatchHSDAddVolumesCommand(hdsApiClient,
systemObjectID, hsdsWithInitiators, volumeURIHLUs, storage.getModel());
if (null != allHSDPaths && !allHSDPaths.isEmpty()) {
updateExportMaskDetailInDB(hsdsWithInitiators, allHSDPaths,
exportMask, storage, volumeURIHLUs);
}
}
taskCompleter.ready(dbClient);
} catch (Exception ex) {
// HSD creation failed before updating exportmask.
// we should rollback them.
// roll back HSDs when there is a failure during adding of
// initiators/volumes.
try {
log.info("Exception occurred while processing exportmask due to: {}", ex.getMessage());
if (null != hsdsWithInitiators && !hsdsWithInitiators.isEmpty()) {
hdsApiClient.getHDSBatchApiExportManager()
.deleteBatchHostStorageDomains(systemObjectID,
hsdsWithInitiators, storage.getModel());
} else {
if (null != hsdsToCreate && !hsdsToCreate.isEmpty()) {
List<HostStorageDomain> allHSDs = hdsApiClient
.getHDSApiExportManager()
.getHostStorageDomains(systemObjectID);
List<HostStorageDomain> partialHSDListToRemove = getPartialHSDListToDelete(
allHSDs, hsdsToCreate);
hdsApiClient.getHDSBatchApiExportManager()
.deleteBatchHostStorageDomains(systemObjectID,
partialHSDListToRemove, storage.getModel());
}
}
log.error(String.format("createExportMask failed - maskName: %s",
exportMaskId.toString()), ex);
} catch (Exception ex1) {
log.error(
"Exception occurred while deleting unsuccessful HSDs on system: {}",
systemObjectID, ex1.getMessage());
} finally {
ServiceError serviceError = DeviceControllerException.errors
.jobFailed(ex);
taskCompleter.error(dbClient, serviceError);
}
}
log.info("{} createExportMask END...", storage.getSerialNumber());
}
/**
* Updates ExportMask details like volumes, HSD's & target port details in DB.
*
* @param hsdsWithInitiators
* : HSD's create successfully.
* @param allHSDPaths
* : Volume LunPaths added successfully.
* @param exportMask
* : ExportMask db object.
* @param storage
* : StorageSystem db object.
* @param volumeURIHLUs
* : volume-lun details.
*/
private void updateExportMaskDetailInDB(List<HostStorageDomain> hsdsWithInitiators,
List<Path> allHSDPaths, ExportMask exportMask, StorageSystem storage,
VolumeURIHLU[] volumeURIHLUs) {
StringSetMap deviceDataMap = new StringSetMap();
for (HostStorageDomain hsd : hsdsWithInitiators) {
StringSet targetPortSet = new StringSet();
List<String> hsdPorts = Arrays.asList(hsd.getPortID());
List<String> targetPortURIs = getStoragePortURIs(hsdPorts, storage);
targetPortSet.addAll(targetPortURIs);
deviceDataMap.put(hsd.getObjectID(), targetPortSet);
}
exportMask.addDeviceDataMap(deviceDataMap);
updateVolumeHLUInfo(volumeURIHLUs, allHSDPaths, exportMask);
dbClient.updateObject(exportMask);
log.info("ExportMask: {} details updated successfully.", exportMask.getId());
}
/**
* Returns a list of HostStorageDomain objects created partially from a
* batch operation.
*
* @param allHSDs
* @param hsdsToCreate
* @return
*/
private List<HostStorageDomain> getPartialHSDListToDelete(
List<HostStorageDomain> allHSDs, List<HostStorageDomain> hsdsToCreate) {
Collection<String> hsdNickNamesList = Collections2.transform(hsdsToCreate,
HDSUtils.fctnHSDToNickName());
List<HostStorageDomain> partialHSDsCreated = new ArrayList<HostStorageDomain>();
if (null != allHSDs && !allHSDs.isEmpty()) {
for (HostStorageDomain hsd : allHSDs) {
if (hsdNickNamesList.contains(hsd.getNickname())) {
partialHSDsCreated.add(hsd);
}
}
}
return partialHSDsCreated;
}
/**
* This routine will take care of following items.
* 1. Prepares a batch of Path objects with volumes & HSD's to add.
* 2. Executes the batch operation.
*
* @param hdsApiClient
* @param systemId
* @param hsdsWithInitiators
* @param volumeURIHLUs
* @param model
* @return
* @throws Exception
*/
private List<Path> executeBatchHSDAddVolumesCommand(HDSApiClient hdsApiClient,
String systemId, List<HostStorageDomain> hsdsWithInitiators,
VolumeURIHLU[] volumeURIHLUs, String model) throws Exception {
if (null == hsdsWithInitiators || hsdsWithInitiators.isEmpty()) {
log.error("Batch HSD creation failed. Aborting operation...");
throw HDSException.exceptions.notAbleToAddHSD(systemId);
}
List<Path> pathList = new ArrayList<Path>();
for (HostStorageDomain hsd : hsdsWithInitiators) {
Map<String, String> volumeLunMap = getVolumeLunMap(systemId,
hsd.getObjectID(), volumeURIHLUs,
hdsApiClient.getHDSApiExportManager());
for (Map.Entry<String, String> entry : volumeLunMap.entrySet()) {
Path path = new Path(hsd.getPortID(), hsd.getDomainID(), null,
entry.getValue(), entry.getKey());
pathList.add(path);
}
}
return hdsApiClient.getHDSBatchApiExportManager().addLUNPathsToHSDs(systemId,
pathList, model);
}
/**
* This routine will take care of following items.
* 1. Prepares a batch of HostStorageDomain objects with initiators to add.
* 2. Executes the batch operation.
*
* @param hdsApiClient
* @param systemObjectID
* @param createHsdsResponseList
* @param storagePorts
* @param initiators
* @return
* @throws Exception
*/
private List<HostStorageDomain> executeBatchHSDAddInitiatorsCommand(HDSApiClient hdsApiClient, String systemObjectID,
List<HostStorageDomain> createHsdsResponseList, List<StoragePort> storagePorts, List<Initiator> initiators, String model)
throws Exception {
List<HostStorageDomain> fcHsdsToAddInitiators = new ArrayList<HostStorageDomain>();
List<HostStorageDomain> iSCSIHsdsToAddInitiators = new ArrayList<HostStorageDomain>();
List<HostStorageDomain> hsdsWithAddIniResponseList = new ArrayList<HostStorageDomain>();
// Considers the IVR Networks as well.
Map<URI, Set<String>> networkInitiatorsMap = NetworkUtil.getNetworkToInitiators(dbClient, initiators);
Map<HostStorageDomain, URI> networkToHsdObjectIdMap = getHostGroupNetworkIdMap(storagePorts, createHsdsResponseList, dbClient);
log.info("networkInitiatorsMap: {}", networkInitiatorsMap);
log.info("networkToHsdObjectIdMap :{}", networkToHsdObjectIdMap);
// Step 2: Add initiators to all HSD's using batch operation
for (Entry<HostStorageDomain, URI> hsdNetworkEntry : networkToHsdObjectIdMap.entrySet()) {
HostStorageDomain hsd = hsdNetworkEntry.getKey();
log.info("Processing hsd: {}", hsd.getObjectID());
HostStorageDomain hsdToAddInitiators = new HostStorageDomain(hsdNetworkEntry.getKey());
Set<String> initiatorsOnSameNetwork = networkInitiatorsMap.get(hsdNetworkEntry.getValue());
// Get the initiators part of the storagePort's Network
List<String> formattedInitiators = getFormattedInitiators(initiatorsOnSameNetwork);
if (hsd.getDomainType().equalsIgnoreCase(HDSConstants.HOST_GROUP_DOMAIN_TYPE)) {
List<WorldWideName> wwnList = new ArrayList(Collections2.transform(
formattedInitiators, HDSUtils.fctnPortWWNToWorldWideName()));
hsdToAddInitiators.setWwnList(wwnList);
fcHsdsToAddInitiators.add(hsdToAddInitiators);
}
if (hsd.getDomainType().equalsIgnoreCase(
HDSConstants.ISCSI_TARGET_DOMAIN_TYPE)) {
List<ISCSIName> iscsiNameList = new ArrayList(Collections2.transform(
formattedInitiators, HDSUtils.fctnPortNameToISCSIName()));
hsdToAddInitiators.setIscsiList(iscsiNameList);
iSCSIHsdsToAddInitiators.add(hsdToAddInitiators);
}
}
if (!fcHsdsToAddInitiators.isEmpty()) {
hsdsWithAddIniResponseList.addAll(hdsApiClient.getHDSBatchApiExportManager()
.addWWNsToHostStorageDomain(systemObjectID, fcHsdsToAddInitiators, model));
}
if (!iSCSIHsdsToAddInitiators.isEmpty()) {
hsdsWithAddIniResponseList.addAll(hdsApiClient.getHDSBatchApiExportManager()
.addISCSINamesToHostStorageDomain(systemObjectID,
iSCSIHsdsToAddInitiators, model));
}
if (null == hsdsWithAddIniResponseList || hsdsWithAddIniResponseList.isEmpty()) {
log.error("Batch add initiators to HSD creation failed. Aborting operation...");
throw HDSException.exceptions
.notAbleToAddInitiatorsToHostStorageDomain(systemObjectID);
}
return hsdsWithAddIniResponseList;
}
/**
* Constructs a map of [Host Group => Network Id] for the given storage ports.
*
* @param sports
* @param hsds
* @param dbClient
* @return
*/
private static Map<HostStorageDomain, URI> getHostGroupNetworkIdMap(
List<StoragePort> sports, List<HostStorageDomain> hsds, DbClient dbClient) {
Map<HostStorageDomain, URI> networkToHSDMap = new HashMap<HostStorageDomain, URI>();
for (StoragePort sport : sports) {
NetworkLite network = NetworkUtil.getEndpointNetworkLite(sport.getPortNetworkId(), dbClient);
if (null != network) {
if (network != null && network.getInactive() == false
&& network.getTransportType().equals(sport.getTransportType())) {
HostStorageDomain hsd = findStoragePortOfHSD(hsds, sport);
if (null != hsd) {
log.info("Found a matching network {} for HSD {}", network.getLabel(), sport.getNativeGuid());
networkToHSDMap.put(hsd, network.getId());
} else {
log.error("Couldn't find the HSD configured for port: {}", sport.getNativeGuid());
}
}
}
}
return networkToHSDMap;
}
private List<String> getFormattedInitiators(Set<String> initiators) {
List<String> formattedInitiators = new ArrayList<String>();
if (null != initiators && !initiators.isEmpty()) {
for (String initiator : initiators) {
if (WWNUtility.isValidWWN(initiator)) {
formattedInitiators.add(initiator.replace(HDSConstants.COLON, HDSConstants.DOT_OPERATOR));
} else {
formattedInitiators.add(initiator);
}
}
}
return formattedInitiators;
}
/**
* Find the storagePort in which the HSD is created.
*
* @param hsd
* @param storagePorts
* @return
*/
private static HostStorageDomain findStoragePortOfHSD(List<HostStorageDomain> hsds, StoragePort storagePort) {
for (HostStorageDomain hsd : hsds) {
if (HDSUtils.getPortID(storagePort).equalsIgnoreCase(hsd.getPortID())) {
log.info("Found matching port for the HSD: {} {}", storagePort.getNativeGuid(), hsd.getObjectID());
return hsd;
}
}
return null;
}
/**
* This routine iterates through the target ports and prepares a batch of HostStorageDomain
* Objects with required information.
*
* @param storage
* @param targetURIList
* @param hostName
* @param exportMask
* @param hostModeInfo
* @return
*/
private List<HostStorageDomain> processTargetPortsToFormHSDs(
HDSApiClient hdsApiClient, StorageSystem storage, List<URI> targetURIList,
String hostName, ExportMask exportMask, Pair<String, String> hostModeInfo,
String systemObjectID) throws Exception {
List<HostStorageDomain> hsdList = new ArrayList<HostStorageDomain>();
String hostMode = null, hostModeOption = null;
if (hostModeInfo != null) {
hostMode = hostModeInfo.first;
hostModeOption = hostModeInfo.second;
}
for (URI targetPortURI : targetURIList) {
StoragePort storagePort = dbClient.queryObject(StoragePort.class,
targetPortURI);
String storagePortNumber = getStoragePortNumber(storagePort.getNativeGuid());
DataSource dataSource = dataSourceFactory.createHSDNickNameDataSource(
hostName, storagePortNumber, storage);
// Hitachi allows only 32 chars as nickname, we should trim the
// length 32 chars.
String hsdNickName = customConfigHandler.getComputedCustomConfigValue(
CustomConfigConstants.HDS_HOST_STORAGE_DOMAIN_NICKNAME_MASK_NAME,
storage.getSystemType(), dataSource);
// If there are any iSCSI initiators then create iSCSI
// HostStorageDomain.
if (Transport.IP.name().equalsIgnoreCase(storagePort.getTransportType())) {
log.info("Populating iSCSI HSD for storage: {}",
storage.getSerialNumber());
HostStorageDomain hostGroup = new HostStorageDomain(storagePortNumber,
exportMask.getMaskName(), HDSConstants.ISCSI_TARGET_DOMAIN_TYPE,
hsdNickName);
hostGroup.setHostMode(hostMode);
hostGroup.setHostModeOption(hostModeOption);
hsdList.add(hostGroup);
}
// If there are FC initiators then create a FC HostStorageDomain.
if (Transport.FC.name().equalsIgnoreCase(storagePort.getTransportType())) {
log.info("Populating FC HSD for storage: {}", storage.getSerialNumber());
HostStorageDomain hostGroup = new HostStorageDomain(storagePortNumber,
exportMask.getMaskName(), HDSConstants.HOST_GROUP_DOMAIN_TYPE,
hsdNickName);
hostGroup.setHostMode(hostMode);
hostGroup.setHostModeOption(hostModeOption);
hsdList.add(hostGroup);
}
}
return hsdList;
}
/**
*
* @param portURIList
* @return
*/
private boolean checkIfMixedTargetPortTypeSelected(List<StoragePort> ports) {
boolean isFC = false;
boolean isIP = false;
for (StoragePort port : ports) {
if (port.getPortType().equalsIgnoreCase(Transport.FC.name())) {
isFC = true;
} else if (port.getPortType().equalsIgnoreCase(Transport.IP.name())) {
isIP = true;
}
if (isFC && isIP) {
return true;
}
}
return false;
}
/**
* return the hostName in which the initiator belongs to.
*
* @param initiatorList
* @return
*/
private String getHostNameForInitiators(List<Initiator> initiatorList) {
Initiator initiator = initiatorList.get(0);
return initiator.getHostName();
}
/**
* Return the host mode & host mode option pair based on the host operating system.
* Only for HUS VM, VSP, USPV, USP, VSP G1000, different host modes
* are supported. Refer API reference guide for details.
*
* @param initiatorList
* @return
*/
private Pair<String, String> getHostModeInfo(StorageSystem storage, List<Initiator> initiatorList) {
// For AMS, WMS, HUS series, only Standard mode is applicable.
// Return null will default to Standard mode.
if (hostModeSupportedModels.contains(storage.getModel())) {
String hostMode = null;
String hostModeOption = null;
Initiator initiator = initiatorList.get(0);
// We don't define Host for vplex initiators connected to backend storage.
// Hence we should check for not null.
if (null != initiator.getHost()) {
Host host = dbClient.queryObject(Host.class, initiator.getHost());
hostMode = HitachiHostMode.getHostMode(host.getType());
if (isHostModeOptionSupported(host.getType())) {
hostModeOption = customConfigHandler.getComputedCustomConfigValue(
CustomConfigConstants.HDS_HOST_STORAGE_HOST_MODE_OPTION, host.getType(), null);
}
}
return new Pair<String, String>(hostMode, hostModeOption);
}
return null;
}
/**
* Return true if host mode option is supported for the given host type.
* Currently supported host models are Windows, Linux, ESX, AIX
*
* @param hostType
* @return
*/
private boolean isHostModeOptionSupported(String hostType) {
return hostType.equalsIgnoreCase(Host.HostType.Windows.name()) ||
hostType.equalsIgnoreCase(Host.HostType.Linux.name()) ||
hostType.equalsIgnoreCase(Host.HostType.AIX.name()) ||
hostType.equalsIgnoreCase(Host.HostType.Esx.name()) ||
hostType.equalsIgnoreCase(Host.HostType.HPUX.name());
}
/**
* Registers all the hosts and its initiators with Device Manager.
*
* @param initiatorList
* @param hdsApiClient
* @param exportMgr
* @return
* @throws Exception
*/
private void registerHostsWithInitiators(List<Initiator> initiatorList,
HDSApiClient hdsApiClient) throws Exception {
Map<HDSHost, List<String>> fcHostsToRegister = new HashMap<HDSHost, List<String>>();
Map<HDSHost, List<String>> iSCSIHostsToRegister = new HashMap<HDSHost, List<String>>();
HDSApiExportManager exportMgr = hdsApiClient.getHDSApiExportManager();
if (null != initiatorList) {
for (Initiator initiator : initiatorList) {
if (null != initiator.getHost()) {
Host host = dbClient.queryObject(Host.class, initiator.getHost());
if (null != host) {
// Get host type and OS type of the host
Pair<String, String> hostTypeAndOsType = getHostTypeAndOSTypeToRegisterHost(host);
HDSHost hdshost = new HDSHost(host.getHostName());
hdshost.setHostType(hostTypeAndOsType.first);
hdshost.setOsType(hostTypeAndOsType.second);
String portWWN = initiator.getInitiatorPort().replace(
HDSConstants.COLON, HDSConstants.DOT_OPERATOR);
if (initiator.getProtocol().equals(HostInterface.Protocol.FC.name())) {
if (!fcHostsToRegister.containsKey(hdshost)) {
List<String> initiatorsList = new ArrayList<String>();
initiatorsList.add(portWWN);
fcHostsToRegister.put(hdshost, initiatorsList);
} else {
fcHostsToRegister.get(hdshost).add(portWWN);
}
} else if (initiator.getProtocol().equals(HostInterface.Protocol.iSCSI.name())) {
if (!iSCSIHostsToRegister.containsKey(hdshost)) {
List<String> initiatorsList = new ArrayList<String>();
initiatorsList.add(portWWN);
iSCSIHostsToRegister.put(hdshost, initiatorsList);
} else {
iSCSIHostsToRegister.get(hdshost).add(portWWN);
}
}
}
}
}
}
if (!fcHostsToRegister.isEmpty()) {
for (Map.Entry<HDSHost, List<String>> entry : fcHostsToRegister.entrySet()) {
HDSHost hdshost = entry.getKey();
// Try registering the host, if host already exists, we will get an exception.
// Validating host and its portwwns is costly hence directly adding the host to Device Manager.
HDSHost registeredHost = exportMgr.registerHost(hdshost, entry.getValue(), HDSConstants.FC);
if (null != registeredHost) {
log.info("Host: {} added successfully.", registeredHost.getName());
}
}
}
if (!iSCSIHostsToRegister.isEmpty()) {
for (Map.Entry<HDSHost, List<String>> entry : iSCSIHostsToRegister.entrySet()) {
HDSHost hdshost = entry.getKey();
// Try registering the host, if host already exists, we will get an exception.
// Validating host and its portwwns is costly hence directly adding the host to Device Manager.
HDSHost registeredHost = exportMgr.registerHost(hdshost, entry.getValue(), HDSConstants.ISCSI);
if (null != registeredHost) {
log.info("Host: {} added successfully.", registeredHost.getName());
}
}
}
}
/**
* Returns the host type and OS type for the given host
*
* @param host
* @return
*/
private Pair<String, String> getHostTypeAndOSTypeToRegisterHost(Host host) {
String hostType = null, osType = null;
if (Host.HostType.Windows.name().equalsIgnoreCase(host.getType()) ||
Host.HostType.Linux.name().equalsIgnoreCase(host.getType()) ||
Host.HostType.AIX.name().equalsIgnoreCase(host.getType())) {
osType = host.getType();
} else if (Host.HostType.HPUX.name().equalsIgnoreCase(host.getType())) {
osType = HDSConstants.HICOMMAND_OS_TYPE_HPUX;
} else if (Host.HostType.Esx.name().equalsIgnoreCase(host.getType())) {
hostType = HDSConstants.HICOMMAND_HOST_TYPE_VMWARE;
}
return new Pair<String, String>(hostType, osType);
}
/**
* Updates the HLU information in the exportmask.
*
* @param volumeURIHLUs
* @param pathList
* @param exportMask
*/
private void updateVolumeHLUInfo(VolumeURIHLU[] volumeURIHLUs, List<Path> pathList, ExportMask exportMask) {
if (null != exportMask.getVolumes() && !exportMask.getVolumes().isEmpty()) {
Map<String, URI> deviceIdToURI = new HashMap<String, URI>();
Map<URI, Integer> hluMap = new HashMap<URI, Integer>();
for (VolumeURIHLU vuh : volumeURIHLUs) {
BlockObject volume = BlockObject.fetch(dbClient, vuh.getVolumeURI());
exportMask.addToUserCreatedVolumes(volume);
deviceIdToURI.put(volume.getNativeId(), volume.getId());
}
if (!deviceIdToURI.isEmpty()) {
for (Path path : pathList) {
if (deviceIdToURI.containsKey(path.getDevNum())) {
URI volumeURI = deviceIdToURI.get(path.getDevNum());
log.info("updating volume {} info in exportmask.", volumeURI);
hluMap.put(volumeURI, Integer.parseInt(path.getLun()));
}
}
exportMask.addVolumes(hluMap);
} else {
log.error("No HLU's found for the volumes.");
}
}
}
/**
*
* @param volumeURIHLUs
* @return
* @throws Exception
*/
private Map<String, String> getVolumeLunMap(String systemId, String hsdObjectID, VolumeURIHLU[] volumeURIHLUs,
HDSApiExportManager exportMgr) throws Exception {
List<FreeLun> freeLunList = exportMgr.getFreeLUNInfo(systemId, hsdObjectID);
log.info("There are {} freeLUN's available for HSD Id {}", freeLunList.size(), hsdObjectID);
Map<String, String> volumeHLUMap = new HashMap<String, String>();
int i = 0;
String lun = null;
for (VolumeURIHLU volumeURIHLU : volumeURIHLUs) {
BlockObject volume = BlockObject.fetch(dbClient, volumeURIHLU.getVolumeURI());
if (null == volumeURIHLU.getHLU() || "-1".equalsIgnoreCase(volumeURIHLU.getHLU())) {
if (i < freeLunList.size()) {
FreeLun freeLun = freeLunList.get(i);
lun = freeLun.getLun();
log.info("HLU is unassigned for volume {} and assinging lun {} from array.", volumeURIHLU.getVolumeURI(), lun);
i++;
} else {
// received request to create more volumes than the free luns available on the array.
log.info("No free LUN's available on HSD {}", hsdObjectID);
throw HDSException.exceptions.unableToProcessRequestDueToUnavailableFreeLUNs();
}
} else {
log.info("HLU {} is assigned for volume {} and using the free lun from array.", volumeURIHLU.getHLU(),
volumeURIHLU.getVolumeURI());
lun = volumeURIHLU.getHLU();
}
volumeHLUMap.put(volume.getNativeId(), lun);
}
return volumeHLUMap;
}
/**
* Return the PortId from port nativeGuid.
*
* @param nativeGuid
* @return
*/
private String getStoragePortNumber(String nativeGuid) {
Iterable<String> portNativeGuidSplitter = Splitter.on(HDSConstants.DOT_OPERATOR).limit(6).split(nativeGuid);
return Iterables.getLast(portNativeGuidSplitter);
}
@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());
List<HostStorageDomain> hsdToDeleteList = new ArrayList<HostStorageDomain>();
try {
log.info("deleteExportMask: 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);
HDSApiClient hdsApiClient = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage), storage.getSmisUserName(),
storage.getSmisPassword());
HDSApiExportManager exportMgr = hdsApiClient.getHDSApiExportManager();
String systemObjectId = HDSUtils.getSystemObjectID(storage);
StringSetMap deviceDataMap = exportMask.getDeviceDataMap();
if (null != deviceDataMap && !deviceDataMap.isEmpty()) {
Set<String> hsdObjectIdList = deviceDataMap.keySet();
for (String hsdObjectIdFromDb : hsdObjectIdList) {
HostStorageDomain hsdObj = exportMgr
.getHostStorageDomain(systemObjectId, hsdObjectIdFromDb);
if (null != hsdObj) {
hsdToDeleteList.add(hsdObj);
}
}
if (!hsdToDeleteList.isEmpty()) {
hdsApiClient.getHDSBatchApiExportManager()
.deleteBatchHostStorageDomains(systemObjectId,
hsdToDeleteList, storage.getModel());
}
// By this time, we have removed all HSD's created in this mask.
taskCompleter.ready(dbClient);
} else {
String message = String.format("ExportMask %s does not have a configured HSD's, "
+ "indicating that this export may not have been created "
+ "successfully. Marking the delete operation ready.",
exportMaskURI.toString());
log.info(message);
taskCompleter.ready(dbClient);
return;
}
} catch (Exception e) {
log.error("Unexpected error: deleteExportMask failed.", e);
ServiceError error = DeviceControllerErrors.hds.methodFailed("deleteExportMask", e.getMessage());
taskCompleter.error(dbClient, error);
}
log.info("{} deleteExportMask END...", storage.getSerialNumber());
}
@Override
public void addVolumes(StorageSystem storage, URI exportMaskURI,
VolumeURIHLU[] volumeURIHLUs, List<Initiator> initiatorList, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} addVolumes START...", storage.getSerialNumber());
HDSApiClient hdsApiClient = null;
String systemObjectID = null;
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));
}
hdsApiClient = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage),
storage.getSmisUserName(), storage.getSmisPassword());
HDSApiExportManager exportMgr = hdsApiClient
.getHDSApiExportManager();
systemObjectID = HDSUtils.getSystemObjectID(storage);
ExportMask exportMask = dbClient.queryObject(ExportMask.class,
exportMaskURI);
StringSetMap deviceDataMap = exportMask.getDeviceDataMap();
Set<String> hsdList = deviceDataMap.keySet();
if (null == hsdList || hsdList.isEmpty()) {
throw HDSException.exceptions
.notAbleToFindHostStorageDomain(systemObjectID);
}
if (null != exportMask && !exportMask.getInactive()
&& !hsdList.isEmpty()) {
List<Path> pathList = new ArrayList<Path>();
for (String hsdObjectID : hsdList) {
// Query the provider to see whether the HSD exists or not.
HostStorageDomain hsd = exportMgr.getHostStorageDomain(
systemObjectID, hsdObjectID);
if (null != hsd) {
Map<String, String> volumeLunMap = getVolumeLunMap(
systemObjectID, hsd.getObjectID(),
volumeURIHLUs, exportMgr);
for (Map.Entry<String, String> entry : volumeLunMap
.entrySet()) {
if (!checkIfVolumeAlreadyExistsOnHSD(
entry.getKey(), hsd)) {
Path path = new Path(hsd.getPortID(),
hsd.getDomainID(), null,
entry.getValue(), entry.getKey());
pathList.add(path);
}
}
}
}
List<Path> pathResponseList = hdsApiClient
.getHDSBatchApiExportManager().addLUNPathsToHSDs(
systemObjectID, pathList, storage.getModel());
if (null != pathResponseList && !pathResponseList.isEmpty()) {
// update volume-lun relationship to exportmask.
updateVolumeHLUInfo(volumeURIHLUs, pathResponseList,
exportMask);
dbClient.updateObject(exportMask);
} else {
log.error(
String.format("addVolumes failed - maskURI: %s",
exportMaskURI.toString()),
new Exception(
"Not able to parse the response of addLUN from server"));
ServiceError serviceError = DeviceControllerException.errors
.jobFailedOpMsg(
ResourceOperationTypeEnum.ADD_EXPORT_VOLUME
.getName(),
"Not able to parse the response of addLUN from server");
taskCompleter.error(dbClient, serviceError);
return;
}
taskCompleter.ready(dbClient);
}
} catch (Exception e) {
log.error(String.format("addVolumes failed - maskURI: %s", exportMaskURI.toString()), e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
log.info("{} addVolumes END...", storage.getSerialNumber());
}
/**
* Verify whether volume already exists on the HostGroup.
*
* @param devNum
* @param hsd
* @return
*/
private boolean checkIfVolumeAlreadyExistsOnHSD(String devNum, HostStorageDomain hsd) {
boolean isVolumeFound = false;
List<Path> pathList = hsd.getPathList();
if (null != pathList && !pathList.isEmpty()) {
for (Path path : pathList) {
if (path.getDevNum().equalsIgnoreCase(devNum)) {
isVolumeFound = true;
break;
}
}
}
return isVolumeFound;
}
@Override
public void removeVolumes(StorageSystem storage, URI exportMaskURI, List<URI> volumes,
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(volumes));
if (initiatorList != null) {
log.info("removeVolumes: impacted initiators: {}", Joiner.on(",").join(initiatorList));
}
HDSApiClient hdsApiClient = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage), storage.getSmisUserName(),
storage.getSmisPassword());
HDSApiExportManager exportMgr = hdsApiClient.getHDSApiExportManager();
String systemObjectID = HDSUtils.getSystemObjectID(storage);
ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskURI);
if (null == exportMask.getDeviceDataMap()) {
log.info("HSD's are not found in the exportMask {} device DataMap.", exportMask.getId());
taskCompleter.ready(dbClient);
}
StringSetMap deviceDataMap = exportMask.getDeviceDataMap();
Set<String> hsdList = deviceDataMap.keySet();
List<Path> pathObjectIdList = new ArrayList<Path>();
if (null == hsdList || hsdList.isEmpty()) {
throw HDSException.exceptions
.notAbleToFindHostStorageDomain(systemObjectID);
}
if (null != exportMask && !exportMask.getInactive()) {
for (String hsdObjectId : hsdList) {
HostStorageDomain hsd = exportMgr.getHostStorageDomain(
systemObjectID, hsdObjectId);
if (null == hsd) {
log.warn("Couldn't find the HSD {} to remove volume from ExportMask", hsdObjectId);
continue;
}
if (null != hsd.getPathList() && !hsd.getPathList().isEmpty()) {
pathObjectIdList.addAll(getPathObjectIdsFromHsd(hsd,
volumes));
}
}
if (!pathObjectIdList.isEmpty()) {
hdsApiClient.getHDSBatchApiExportManager()
.deleteLUNPathsFromStorageSystem(systemObjectID,
pathObjectIdList, storage.getModel());
} else {
log.info("No volumes found on system: {}", systemObjectID);
}
// Update the status after deleting the volume from all HSD's.
taskCompleter.ready(dbClient);
}
} catch (Exception e) {
log.error(
String.format("removeVolume failed - maskURI: %s",
exportMaskURI.toString()),
e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
log.info("{} removeVolumes END...", storage.getSerialNumber());
}
/**
* Get the LUN Path objectId list from HSD.
* Since we are getting volume URI, we should query to get its nativeId.
*
* @param hsd
* @param volumes
* @return
*/
private List<Path> getPathObjectIdsFromHsd(HostStorageDomain hsd, List<URI> volumes) {
List<Path> pathObjectIdList = new ArrayList<Path>();
if (null != hsd.getPathList()) {
for (URI volumeURI : volumes) {
BlockObject volume = BlockObject.fetch(dbClient, volumeURI);
for (Path path : hsd.getPathList()) {
log.info("verifying existence of volume {} on HSD to remove",
volume.getNativeId());
if (volume.getNativeId().equals(path.getDevNum())) {
log.debug("Found volume {} on HSD {}", volume.getNativeId(), hsd.getObjectID());
pathObjectIdList.add(path);
}
}
}
}
return pathObjectIdList;
}
@Override
public void addInitiators(StorageSystem storage, URI exportMaskURI,
List<URI> volumeURIs, List<Initiator> initiators, List<URI> targetURIList, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("{} addInitiator START...", storage.getSerialNumber());
HDSApiClient hdsApiClient = null;
String systemObjectID = null;
List<HostStorageDomain> hsdsToCreate = null;
List<HostStorageDomain> hsdsWithInitiators = null;
try {
log.info("addInitiator: Export mask id: {}", exportMaskURI);
if (volumeURIs != null) {
log.info("addInitiator: volumes : {}", Joiner.on(',').join(volumeURIs));
}
log.info("addInitiator: initiators : {}", Joiner.on(',').join(initiators));
log.info("addInitiator: targets : {}", Joiner.on(",").join(targetURIList));
hdsApiClient = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage), storage.getSmisUserName(),
storage.getSmisPassword());
HDSApiExportManager exportMgr = hdsApiClient.getHDSApiExportManager();
systemObjectID = HDSUtils.getSystemObjectID(storage);
ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskURI);
List<StoragePort> ports = dbClient.queryObject(StoragePort.class, targetURIList, true);
if (checkIfMixedTargetPortTypeSelected(ports)) {
log.error("Unsupported Host as it has both FC & iSCSI Initiators");
throw HDSException.exceptions.unsupportedConfigurationFoundInHost();
}
// @TODO register new initiators by adding them to host on HiCommand DM.
// Currently, HiCommand is not supporting this. Need to see how we can handle.
String hostName = getHostNameForInitiators(initiators);
String hostMode = null, hostModeOption = null;
Pair<String, String> hostModeInfo = getHostModeInfo(storage, initiators);
if (hostModeInfo != null) {
hostMode = hostModeInfo.first;
hostModeOption = hostModeInfo.second;
}
/*
* There are two cases which we should handle here.
* 1. When new initiator's are added and user increased pathsPerInitiator & maximum paths.
* 2. New initiator's should be added to the existing HSD's to access the volumes which are already part of
* the export mask.
*/
if (targetURIList != null && !targetURIList.isEmpty()) {
Set<URI> newTargetPorts = new HashSet<>(targetURIList);
Set<URI> existingTargetPortsInMask = new HashSet<>();
if (exportMask.getStoragePorts() != null) {
existingTargetPortsInMask = new HashSet<>(targetURIList);
Collection<URI> targetPorts = Collections2.transform(exportMask.getStoragePorts(),
CommonTransformerFunctions.FCTN_STRING_TO_URI);
existingTargetPortsInMask.retainAll(targetPorts);
}
newTargetPorts.removeAll(existingTargetPortsInMask);
log.info("list of new storage target ports {}", newTargetPorts);
log.info("list of existing storage target ports available in export masks {}", existingTargetPortsInMask);
// Case 1 is handled here for the new initiators & new target ports.
if (!newTargetPorts.isEmpty()) {
// If the HLU's are already configured on this target port, then an exception is thrown.
// User should make sure that all volumes should have same HLU across all target HSD's.
VolumeURIHLU[] volumeURIHLUs = getVolumeURIHLUFromExportMask(exportMask);
if (0 < volumeURIHLUs.length) {
List<URI> portList = new ArrayList<>(newTargetPorts);
hsdsToCreate = processTargetPortsToFormHSDs(hdsApiClient, storage,
portList, hostName, exportMask, hostModeInfo, systemObjectID);
// Step 1: Create all HSD's using batch operation.
List<HostStorageDomain> hsdResponseList = hdsApiClient
.getHDSBatchApiExportManager().addHostStorageDomains(
systemObjectID, hsdsToCreate, storage.getModel());
if (null == hsdResponseList || hsdResponseList.isEmpty()) {
log.error("Batch HSD creation failed to add new initiators. Aborting operation...");
throw HDSException.exceptions.notAbleToAddHSD(storage
.getSerialNumber());
}
// Step 2: Add initiators to all HSD's.
Iterator<StoragePort> storagePortIterator = dbClient.queryIterativeObjects(StoragePort.class, newTargetPorts);
List<StoragePort> stPortList = new ArrayList<>();
while (storagePortIterator.hasNext()) {
StoragePort stPort = storagePortIterator.next();
if (stPort != null && !stPort.getInactive()) {
stPortList.add(stPort);
}
}
hsdsWithInitiators = executeBatchHSDAddInitiatorsCommand(hdsApiClient,
systemObjectID, hsdResponseList, stPortList, initiators, storage.getModel());
// Step 3: Add volumes to all HSD's.
List<Path> allHSDPaths = executeBatchHSDAddVolumesCommand(hdsApiClient,
systemObjectID, hsdsWithInitiators, volumeURIHLUs, storage.getModel());
if (null != allHSDPaths && !allHSDPaths.isEmpty()) {
updateExportMaskDetailInDB(hsdsWithInitiators, allHSDPaths,
exportMask, storage, volumeURIHLUs);
}
} else {
log.info("There are no volumes on this exportmask: {} to add to new initiator", exportMaskURI);
}
}
// Case 2 is handled here i.e. adding the new initiator to the
// existing HSD to access the volumes already exported in the exportmask.
if (!existingTargetPortsInMask.isEmpty()) {
// Step 1: Collect all HSDs from export mask
StringSetMap deviceDataMap = exportMask.getDeviceDataMap();
if (null != deviceDataMap && !deviceDataMap.isEmpty()) {
List<HostStorageDomain> hsdResponseList = new ArrayList<>();
Set<String> hsdObjectIdSet = deviceDataMap.keySet();
for (String hsdObjectId : hsdObjectIdSet) {
HostStorageDomain hsd = exportMgr.getHostStorageDomain(systemObjectID, hsdObjectId);
if (null == hsd) {
throw HDSException.exceptions
.notAbleToFindHostStorageDomain(hsdObjectId);
}
hsdResponseList.add(hsd);
}
// Step 2: Add initiators to all HSD's.
Iterator<StoragePort> storagePortIterator = dbClient.queryIterativeObjects(StoragePort.class,
existingTargetPortsInMask, true);
List<StoragePort> stPortList = new ArrayList<>();
while (storagePortIterator.hasNext()) {
StoragePort stPort = storagePortIterator.next();
if (stPort != null) {
stPortList.add(stPort);
}
}
hsdsWithInitiators = executeBatchHSDAddInitiatorsCommand(hdsApiClient,
systemObjectID, hsdResponseList, stPortList, initiators, storage.getModel());
} else {
log.info("There are no hsd information in exportMask to add initiators");
}
}
}
taskCompleter.ready(dbClient);
} catch (Exception ex) {
try {
log.info("Exception occurred while adding new initiators: {}", ex.getMessage());
if (null != hsdsWithInitiators && !hsdsWithInitiators.isEmpty()) {
hdsApiClient.getHDSBatchApiExportManager()
.deleteBatchHostStorageDomains(systemObjectID,
hsdsWithInitiators, storage.getModel());
} else {
if (null != hsdsToCreate && !hsdsToCreate.isEmpty()) {
List<HostStorageDomain> allHSDs = hdsApiClient
.getHDSApiExportManager()
.getHostStorageDomains(systemObjectID);
List<HostStorageDomain> partialHSDListToRemove = getPartialHSDListToDelete(
allHSDs, hsdsToCreate);
hdsApiClient.getHDSBatchApiExportManager()
.deleteBatchHostStorageDomains(systemObjectID,
partialHSDListToRemove, storage.getModel());
}
}
log.error(String.format("addInitiator failed - maskURI: %s",
exportMaskURI.toString()), ex);
} catch (Exception ex1) {
log.error(
"Exception occurred while deleting unsuccessful HSDs on system: {}",
systemObjectID, ex1.getMessage());
} finally {
ServiceError serviceError = DeviceControllerException.errors.jobFailedOpMsg(
ResourceOperationTypeEnum.ADD_EXPORT_INITIATOR.getName(),
ex.getMessage());
taskCompleter.error(dbClient, serviceError);
}
}
log.info("{} addInitiator END...", storage.getSerialNumber());
}
/**
* Return VolumeURIHLU from the exportMask userAddedVolume.
* should we add existing volumes also in case if the environment is not green field?
*
* @param exportMask
* @return
*/
private VolumeURIHLU[] getVolumeURIHLUFromExportMask(ExportMask exportMask) {
VolumeURIHLU[] volumeURIHLU = null;
if (null != exportMask && null != exportMask.getUserAddedVolumes() && !exportMask.getUserAddedVolumes().isEmpty()) {
Set<String> userAddedVolumeSet = new HashSet<String>(exportMask.getUserAddedVolumes().values());
volumeURIHLU = new VolumeURIHLU[userAddedVolumeSet.size()];
int index = 0;
for (String userAddedVolume : userAddedVolumeSet) {
URI userAddedVolumeURI = URI.create(userAddedVolume);
BlockObject volume = BlockObject.fetch(dbClient, userAddedVolumeURI);
if (null != volume && !volume.getInactive()) {
String hlu = exportMask.getVolumes().get(userAddedVolume);
volumeURIHLU[index++] = new VolumeURIHLU(userAddedVolumeURI, hlu, null, volume.getLabel());
}
}
}
return volumeURIHLU;
}
@Override
public void removeInitiators(StorageSystem storage, URI exportMaskURI,
List<URI> volumeURIList, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter)
throws DeviceControllerException {
long startTime = System.currentTimeMillis();
log.info("{} removeInitiator START...", storage.getSerialNumber());
try {
log.info("removeInitiator: Export mask id: {}", exportMaskURI);
if (volumeURIList != null) {
log.info("removeInitiator: volumes : {}", Joiner.on(',').join(volumeURIList));
}
log.info("removeInitiator: initiators : {}", Joiner.on(',').join(initiators));
log.info("removeInitiator: targets : {}", Joiner.on(',').join(targets));
if (null == initiators || initiators.isEmpty()) {
log.info("No initiators found to remove {}", exportMaskURI);
taskCompleter.ready(dbClient);
return;
}
HDSApiClient hdsApiClient = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage), storage.getSmisUserName(),
storage.getSmisPassword());
HDSApiExportManager exportMgr = hdsApiClient.getHDSApiExportManager();
String systemObjectID = HDSUtils.getSystemObjectID(storage);
ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskURI);
StringSetMap deviceDataMap = exportMask.getDeviceDataMap();
if (null != deviceDataMap && !deviceDataMap.isEmpty()) {
Set<String> hsdObjectIDSet = deviceDataMap.keySet();
for (String hsdObjectID : hsdObjectIDSet) {
HostStorageDomain hsd = exportMgr.getHostStorageDomain(
systemObjectID, hsdObjectID);
if (null == hsd) {
log.warn("Not able to remove initiators as HSD {} couldn't find on array.", hsdObjectID);
continue;
}
List<String> fcInitiators = getFCInitiatorsExistOnHSD(hsd, initiators);
List<String> iSCSIInitiators = getISCSIInitiatorsExistOnHSD(hsd, initiators);
boolean isLastFCInitiator = (fcInitiators.size() == 1 && null != hsd.getWwnList() && hsd
.getWwnList().size() == fcInitiators.size());
boolean isLastISCSIInitiator = (iSCSIInitiators.size() == 1 && null != hsd.getIscsiList() && hsd
.getIscsiList().size() == iSCSIInitiators.size());
// If Initiator is last one, remove the HSD
if (isLastFCInitiator || isLastISCSIInitiator) {
exportMgr.deleteHostStorageDomain(systemObjectID, hsd.getObjectID(), storage.getModel());
exportMask.getDeviceDataMap().remove(hsd.getObjectID());
} else {
if (null != fcInitiators && !fcInitiators.isEmpty()) {
// remove FC initiators from HSD.
exportMgr.deleteWWNsFromHostStorageDomain(systemObjectID, hsd.getObjectID(),
fcInitiators, storage.getModel());
}
if (null != iSCSIInitiators && !iSCSIInitiators.isEmpty()) {
// remove ISCSInames from HSD.
exportMgr.deleteISCSIsFromHostStorageDomain(systemObjectID, hsd.getObjectID(),
iSCSIInitiators, storage.getModel());
}
}
}
dbClient.updateObject(exportMask);
// update the task status after processing all HSD's.
taskCompleter.ready(dbClient);
} else {
log.info("No Host groups found on exportMask {}", exportMaskURI);
// No HSD's found in exportMask.
taskCompleter.ready(dbClient);
}
} catch (Exception e) {
log.error(
String.format("removeInitiator failed - maskURI: %s",
exportMaskURI.toString()),
e);
ServiceError serviceError = DeviceControllerException.errors.jobFailedOpMsg(
ResourceOperationTypeEnum.DELETE_EXPORT_INITIATOR.getName(),
e.getMessage());
taskCompleter.error(dbClient, serviceError);
} finally {
long totalTime = System.currentTimeMillis() - startTime;
log.info(String.format("findExportMasks took %f seconds", (double) totalTime / (double) 1000));
}
log.info("{} removeInitiator END...", storage.getSerialNumber());
}
/**
* Return the list of WWN's to be removed from the HSD.
*
* @param hsd
* @param allInitiatorsToRemove
* @return
*/
private List<String> getFCInitiatorsExistOnHSD(HostStorageDomain hsd, List<Initiator> allInitiatorsToRemove) {
List<WorldWideName> hsdWWNList = hsd.getWwnList();
List<String> fcInitiatorsToRemove = new ArrayList<String>();
Collection<String> portNames = Collections2.transform(allInitiatorsToRemove,
CommonTransformerFunctions.fctnInitiatorToPortName());
if (null != hsdWWNList && !hsdWWNList.isEmpty()) {
for (WorldWideName wwn : hsdWWNList) {
if (portNames.contains(wwn.getWwn().replace(
HDSConstants.DOT_OPERATOR, HDSConstants.EMPTY_STR).toUpperCase())) {
fcInitiatorsToRemove.add(wwn.getWwn());
}
}
}
return fcInitiatorsToRemove;
}
/**
* Return the list of ISCSINames to be removed from the HSD.
*
* @param hsd
* @param allInitiatorsToRemove
* @return
*/
private List<String> getISCSIInitiatorsExistOnHSD(HostStorageDomain hsd, List<Initiator> allInitiatorsToRemove) {
List<ISCSIName> hsdISCSIList = hsd.getIscsiList();
List<String> iSCSIInitiatorsToRemove = new ArrayList<String>();
Collection<String> portNames = Collections2.transform(allInitiatorsToRemove,
CommonTransformerFunctions.fctnInitiatorToPortName());
if (null != hsdISCSIList && !hsdISCSIList.isEmpty()) {
for (ISCSIName iSCSIName : hsdISCSIList) {
if (portNames.contains(iSCSIName.getiSCSIName())) {
iSCSIInitiatorsToRemove.add(iSCSIName.getiSCSIName());
}
}
}
return iSCSIInitiatorsToRemove;
}
@Override
public Map<String, Set<URI>> findExportMasks(StorageSystem storage,
List<String> initiatorNames, boolean mustHaveAllPorts) throws DeviceControllerException {
Map<String, Set<URI>> matchingMasks = new HashMap<String, Set<URI>>();
log.info("finding export masks for storage {}", storage.getId());
HDSApiClient client = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage),
storage.getSmisUserName(), storage.getSmisPassword());
HDSApiExportManager exportManager = client.getHDSApiExportManager();
String systemObjectID = HDSUtils.getSystemObjectID(storage);
Map<URI, Set<HostStorageDomain>> matchedHostHSDsMap = new HashMap<URI, Set<HostStorageDomain>>();
Map<URI, Set<URI>> hostToInitiatorMap = new HashMap<URI, Set<URI>>();
Map<URI, Set<String>> matchedHostInitiators = new HashMap<URI, Set<String>>();
try {
List<HostStorageDomain> hsdList = exportManager
.getHostStorageDomains(systemObjectID);
for (HostStorageDomain hsd : hsdList) {
List<String> initiatorsExistsOnHSD = getInitiatorsExistsOnHSD(
hsd.getWwnList(), hsd.getIscsiList());
// Find out if the port is in this masking container
for (String initiatorName : initiatorNames) {
String normalizedName = initiatorName.replace(
HDSConstants.COLON, HDSConstants.DOT_OPERATOR);
if (initiatorsExistsOnHSD.contains(normalizedName)) {
log.info("Found a matching HSD for {}", initiatorName);
Initiator initiator = fetchInitiatorByName(initiatorName);
Set<HostStorageDomain> matchedHSDs = matchedHostHSDsMap
.get(initiator.getHost());
if (matchedHSDs == null) {
matchedHSDs = new HashSet<HostStorageDomain>();
matchedHostHSDsMap.put(initiator.getHost(),
matchedHSDs);
}
matchedHSDs.add(hsd);
Set<String> matchedInitiators = matchedHostInitiators
.get(initiator.getHost());
if (null == matchedInitiators) {
matchedInitiators = new HashSet<String>();
matchedHostInitiators.put(initiator.getHost(),
matchedInitiators);
}
matchedInitiators.add(initiatorName);
}
}
}
hsdList.clear();
log.info("matchedHSDs: {}", matchedHostHSDsMap);
log.info("initiatorURIToNameMap: {}", matchedHostInitiators);
processInitiators(initiatorNames, hostToInitiatorMap);
List<URI> activeMaskIdsInDb = dbClient.queryByType(
ExportMask.class, true);
List<ExportMask> activeMasks = dbClient.queryObject(
ExportMask.class, activeMaskIdsInDb);
if (null != matchedHostHSDsMap && !matchedHostHSDsMap.isEmpty()) {
// Iterate through each host
for (URI hostURI : hostToInitiatorMap.keySet()) {
Set<URI> hostInitiators = hostToInitiatorMap.get(hostURI);
boolean isNewExportMask = false;
//Create single ExportMask for each host-varray combination
List<ExportMask> exportMaskWithHostInitiators = fetchExportMasksFromDB(activeMasks,
hostInitiators, storage);
for (HostStorageDomain hsd : matchedHostHSDsMap.get(hostURI)) {
String storagePortOFHDSURI = getStoragePortURIs(Arrays.asList(hsd.getPortID()), storage).get(0);
ExportMask maskForHSD = null;
for (ExportMask exportMaskhavingInitiators : exportMaskWithHostInitiators) {
if(exportMaskhavingInitiators.getStoragePorts().contains(storagePortOFHDSURI)) {
maskForHSD = exportMaskhavingInitiators;
break;
}
}
if (null == maskForHSD) {
//first get the varrays associated with the storage port of the HSD and then check if
//any of the export masks have storage ports, which have virtual arrays overlapping with the virtual
//arrays of the HSD storage port
//NOTE: If the storageport is assigned to multiple varrays, then maintaining one
//export mask per varray is not possible. Proper seggregation has to be done.
StringSet varraysAssociatedWithHSDStoragePort = getTaggedVarrays(storagePortOFHDSURI);
if(!varraysAssociatedWithHSDStoragePort.isEmpty()){
boolean bMaskFound = false;
for (ExportMask exportMaskhavingInitiators : exportMaskWithHostInitiators) {
for(String storagePortUriIter : exportMaskhavingInitiators.getStoragePorts()) {
//get the storage port entity
StringSet varraysOfStoragePort = getTaggedVarrays(storagePortUriIter);
if (StringSetUtil.hasIntersection(varraysOfStoragePort, varraysAssociatedWithHSDStoragePort)){
maskForHSD = exportMaskhavingInitiators;
//Ingest the foreign HSD into a matching export mask with same host and varray combination
bMaskFound = true;
break;
}
}
if(bMaskFound){
break;
}
}
}
else{
//Since this HSD port is not tagged to any varray, we will not ingest it
continue;
}
if(null == maskForHSD){
//No matching export mask found for the same host and varray combination. Creating a new export mask.
isNewExportMask = true;
maskForHSD = new ExportMask();
maskForHSD.setId(URIUtil.createId(ExportMask.class));
maskForHSD.setStorageDevice(storage.getId());
maskForHSD.setCreatedBySystem(false);
}
}
Set<HostStorageDomain> hsdSet = new HashSet<>();
hsdSet.add(hsd);
updateHSDInfoInExportMask(maskForHSD, hostInitiators, hsdSet, storage, matchingMasks);
if (isNewExportMask) {
dbClient.createObject(maskForHSD);
exportMaskWithHostInitiators.add(maskForHSD);
} else {
ExportMaskUtils.sanitizeExportMaskContainers(dbClient, maskForHSD);
dbClient.updateAndReindexObject(maskForHSD);
}
updateMatchingMasksForHost(
matchedHostInitiators.get(hostURI), maskForHSD,
matchingMasks);
}
}
}
} catch (Exception e) {
log.error("Error when attempting to query LUN masking information",
e);
throw HDSException.exceptions.queryExistingMasksFailure(e
.getMessage());
}
log.info("Found matching masks: {}", matchingMasks);
return matchingMasks;
}
/**
* Updates MatchingMasks and return to the Orchestrator to act on on the matchingMasks.
* Orchestrator will decide whether to add new volumes/initiators based on this collection.
*
* @param matchedHostInitiators
* @param exportMask
* @param matchingMasks
*/
private void updateMatchingMasksForHost(Set<String> matchedHostInitiators,
ExportMask exportMask, Map<String, Set<URI>> matchingMasks) {
if (null != matchedHostInitiators && !matchedHostInitiators.isEmpty()) {
for (String initiatorName : matchedHostInitiators) {
Set<URI> maskURIs = matchingMasks.get(initiatorName);
if (maskURIs == null) {
maskURIs = new HashSet<URI>();
matchingMasks.put(initiatorName, maskURIs);
}
// Assuming all initiators are configured
maskURIs.add(exportMask.getId());
}
}
}
/**
* Returns the set of taggervirtualArray arrays for the storage port object.
*
* @param storagePortId
* @return string set of tagged virtual arrays
*/
private StringSet getTaggedVarrays(String storagePortId){
URI storagePortURI = URI.create(storagePortId);
StoragePort objStoragePort = dbClient.queryObject(StoragePort.class, storagePortURI);
if(objStoragePort != null){
if( (objStoragePort.getTaggedVirtualArrays() != null) &&
(!objStoragePort.getTaggedVirtualArrays().isEmpty()) ){
return objStoragePort.getTaggedVirtualArrays();
}
}
return new StringSet();
}
/**
* Updates the HSD information in the ExportMask.
*
* @param exportMask
* @param hostInitiators
* @param matchedHSDs
*/
private void updateHSDInfoInExportMask(ExportMask exportMask,
Set<URI> hostInitiators, Set<HostStorageDomain> matchedHSDs,
StorageSystem storage, Map<String, Set<URI>> matchingMasks) {
StringBuilder builder = new StringBuilder();
if (null != matchedHSDs && !matchedHSDs.isEmpty()) {
StringSetMap deviceDataMapEntries = new StringSetMap();
for (HostStorageDomain matchedHSD : matchedHSDs) {
List<String> initiatorsExistsOnHSD = getInitiatorsExistsOnHSD(
matchedHSD.getWwnList(), matchedHSD.getIscsiList());
// volume Map['volume Id'] = lun
Map<String, Integer> volumesExistsOnHSD = getExportedVolumes(
matchedHSD.getPathList(), storage);
log.info("Current list of Volumes exists on this HSD: {}",
volumesExistsOnHSD);
builder.append(String.format("XM:%s I:{%s} V:{%s}%n",
matchedHSD.getObjectID(),
Joiner.on(',').join(initiatorsExistsOnHSD),
Joiner.on(',').join(volumesExistsOnHSD.keySet())));
// Update deviceDataMap in ExportMask with HSD information.
updateDeviceDataMapInExportMask(exportMask, matchedHSD, storage, deviceDataMapEntries);
List<String> storagePorts = Arrays.asList(matchedHSD
.getPortID());
List<String> storagePortURIs = getStoragePortURIs(storagePorts,
storage);
if (null != exportMask.getStoragePorts()
&& !exportMask.getStoragePorts().isEmpty()) {
// Add the storage ports if they already exist on
// exportmask.
exportMask.getStoragePorts().addAll(storagePortURIs);
} else {
exportMask.setStoragePorts(storagePortURIs);
}
String maskName = (null == matchedHSD.getName()) ? matchedHSD
.getNickname() : matchedHSD.getName();
exportMask.setMaskName(maskName);
exportMask.addToExistingVolumesIfAbsent(volumesExistsOnHSD);
exportMask
.addToExistingInitiatorsIfAbsent(initiatorsExistsOnHSD);
List<Initiator> initiatorList = new ArrayList<>();
if (!CollectionUtils.isEmpty(initiatorsExistsOnHSD)) {
for (String port : initiatorsExistsOnHSD) {
Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), dbClient);
if (existingInitiator != null && !existingInitiator.getInactive()) {
initiatorList.add(existingInitiator);
}
}
}
exportMask.addInitiators(initiatorList);
String strExistingInitiators = "";
String strExistingVolumes = "";
if(exportMask.getExistingInitiators() !=null ){
strExistingInitiators = Joiner.on(',').join(
exportMask.getExistingInitiators());
}
if(exportMask.getExistingVolumes() != null){
strExistingVolumes = Joiner.on(',').join(
exportMask.getExistingVolumes()
.keySet());
}
builder.append(String
.format("XM is matching. " + "EI: { %s }, EV: { %s }\n",
strExistingInitiators, strExistingVolumes));
}
exportMask.addDeviceDataMap(deviceDataMapEntries);
}
log.info(builder.toString());
}
/**
* Fetches ExportMasks from DB based on the given initiators.
*
* @param hostInitiators
* @param storage
* @return
*/
private List<ExportMask> fetchExportMasksFromDB(List<ExportMask> activeMasks, Set<URI> hostInitiators, StorageSystem storage) {
List<ExportMask> exportMasks = new ArrayList<>();
if (null != activeMasks && !activeMasks.isEmpty()) {
for (ExportMask activeExportMask : activeMasks) {
if (!activeExportMask.getStorageDevice().equals(storage.getId())) {
continue;
}
Set<URI> emInitiators = ExportMaskUtils.getAllInitiatorsForExportMask(dbClient, activeExportMask);
Set<URI> matchingInitiators = Sets.intersection(emInitiators, hostInitiators);
if (!matchingInitiators.isEmpty()) {
exportMasks.add(activeExportMask);
}
}
}
return exportMasks;
}
/**
* Process the initiators passed in to form a host => initiators data
* structure.
*
* @param initiatorNames
* @param hostToInitiatorMap
*/
private void processInitiators(List<String> initiatorNames, Map<URI, Set<URI>> hostToInitiatorMap) {
for (String initiatorWWNStr : initiatorNames) {
Initiator initiator = fetchInitiatorByName(initiatorWWNStr);
if (null != initiator) {
Set<URI> initiators = hostToInitiatorMap.get(initiator.getHost());
if (initiators == null) {
initiators = new HashSet<URI>();
hostToInitiatorMap.put(initiator.getHost(), initiators);
}
initiators.add(initiator.getId());
}
}
}
/**
* Fetches initiator from DB based on its WWN.
*
* @param initiatorName
* @return
*/
private Initiator fetchInitiatorByName(String initiatorName) {
URIQueryResultList initiatorResultList = new URIQueryResultList();
Initiator initiator = null;
dbClient.queryByConstraint(AlternateIdConstraint.Factory.getInitiatorPortInitiatorConstraint(WWNUtility
.getWWNWithColons(initiatorName)), initiatorResultList);
if (initiatorResultList.iterator().hasNext()) {
initiator = dbClient.queryObject(Initiator.class, initiatorResultList.iterator().next());
}
return initiator;
}
/**
* Updates the DeviceDataMap in the ExportMask for all HSD's.
*
* @param exportMask
* @param hsd
* @param storage
* @param deviceDataMap
* @param deviceDataMapEntries
*/
private void updateDeviceDataMapInExportMask(ExportMask exportMask,
HostStorageDomain hsd, StorageSystem storage,
StringSetMap deviceDataMapEntries) {
List<String> storagePorts = Arrays.asList(hsd.getPortID());
StringSet hsdTargetPortValues = new StringSet();
List<String> storagePortURIs = getStoragePortURIs(storagePorts, storage);
hsdTargetPortValues.addAll(storagePortURIs);
deviceDataMapEntries.put(hsd.getObjectID(), hsdTargetPortValues);
}
/**
* Since we get only port Ids from HSD, we should iterate thru all the ports of the system
* and find its URIs.
*
* @param storagePortsOnExistingHSD
* @param storage
* @return
*/
private List<String> getStoragePortURIs(List<String> storagePortsOnExistingHSD, StorageSystem storage) {
List<String> portURIs = new ArrayList<String>();
URIQueryResultList portURIList = new URIQueryResultList();
dbClient.queryByConstraint(ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(storage.getId()), portURIList);
List<StoragePort> storagePorts = dbClient.queryObject(StoragePort.class, portURIList);
for (StoragePort portFromDB : storagePorts) {
String portNativeIdFromDB = getStoragePortNumber(portFromDB.getNativeGuid());
for (String portNativeIdOnHSD : storagePortsOnExistingHSD) {
if (portNativeIdOnHSD.equals(portNativeIdFromDB)) {
portURIs.add(portFromDB.getId().toString());
}
}
}
return portURIs;
}
/**
* Return the list of initiators that were exists on the HostStorageDomain.
*
* @param wwnList
* @param scsiList
* @return
*/
private List<String> getInitiatorsExistsOnHSD(List<WorldWideName> wwnList, List<ISCSIName> scsiList) {
List<String> initiatorsExistsOnHSD = new ArrayList<String>();
if (null != wwnList && !wwnList.isEmpty()) {
for (WorldWideName wwn : wwnList) {
String wwnWithOutDot = wwn.getWwn().replace(HDSConstants.DOT_OPERATOR, "");
initiatorsExistsOnHSD.add(wwnWithOutDot);
}
}
if (null != scsiList && !scsiList.isEmpty()) {
for (ISCSIName scsiName : scsiList) {
initiatorsExistsOnHSD.add(scsiName.getiSCSIName());
}
}
return initiatorsExistsOnHSD;
}
/**
* Return the [volume-lun] map that exists on the HSD.
*
* @param pathList
* @return
*/
private Map<String, Integer> getExportedVolumes(List<Path> pathList, StorageSystem storage) {
Map<String, Integer> volumeLunMap = new HashMap<String, Integer>();
if (null != pathList && !pathList.isEmpty()) {
for (Path path : pathList) {
String volumeWWN = HDSUtils.generateHitachiVolumeWWN(storage, path.getDevNum());
volumeLunMap.put(volumeWWN, Integer.valueOf(path.getLun()));
}
}
return volumeLunMap;
}
@Override
public Set<Integer> findHLUsForInitiators(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) {
// TODO Auto-generated method stub
return null;
}
@Override
public ExportMask refreshExportMask(StorageSystem storage, ExportMask mask) throws DeviceControllerException {
try {
HDSApiClient client = hdsApiFactory.getClient(
getHDSServerManagementServerInfo(storage), storage.getSmisUserName(),
storage.getSmisPassword());
HDSApiExportManager exportManager = client.getHDSApiExportManager();
String systemObjectID = HDSUtils.getSystemObjectID(storage);
if (null != mask.getDeviceDataMap() && !mask.getDeviceDataMap().isEmpty()) {
Set<String> hsdList = mask.getDeviceDataMap().keySet();
StringBuilder builder = new StringBuilder();
Set<String> discoveredInitiators = new HashSet<String>();
String maskName = null;
Map<String, Integer> discoveredVolumes = new HashMap<String, Integer>();
for (String hsdObjectIdFromDb : hsdList) {
HostStorageDomain hsd = exportManager.getHostStorageDomain(
systemObjectID, hsdObjectIdFromDb);
if (null == hsd) {
// If the HSD is removed using non-ViPR, update ViPR DB.
mask.getDeviceDataMap().remove(hsdObjectIdFromDb);
continue;
}
maskName = (null == hsd.getName()) ? hsd.getNickname() : hsd.getName();
// Get volumes and initiators for the masking instance
discoveredVolumes.putAll(getVolumesFromHSD(hsd, storage));
discoveredInitiators.addAll(getInitiatorsFromHSD(hsd));
}
Set existingInitiators = (mask.getExistingInitiators() != null) ? mask.getExistingInitiators() : Collections.emptySet();
Set existingVolumes = (mask.getExistingVolumes() != null) ? mask.getExistingVolumes().keySet() : Collections.emptySet();
builder.append(String.format("%nXM object: %s I{%s} V:{%s}%n", maskName,
Joiner.on(',').join(existingInitiators), Joiner.on(',').join(existingVolumes)));
builder.append(String.format("XM discovered: %s I:{%s} V:{%s}%n", maskName,
Joiner.on(',').join(discoveredInitiators), Joiner.on(',').join(discoveredVolumes.keySet())));
List<Initiator> initiatorList = new ArrayList<>();
if (!CollectionUtils.isEmpty(discoveredInitiators)) {
for (String port : discoveredInitiators) {
Initiator existingInitiator = ExportUtils.getInitiator(Initiator.toPortNetworkId(port), dbClient);
if (existingInitiator != null && !existingInitiator.getInactive()) {
initiatorList.add(existingInitiator);
}
}
}
mask.addInitiators(initiatorList);
dbClient.updateObject(mask);
// Check the initiators and update the lists as necessary
boolean addInitiators = false;
List<String> initiatorsToAdd = new ArrayList<String>();
for (String initiator : discoveredInitiators) {
if (!mask.hasExistingInitiator(initiator) && !mask.hasUserInitiator(initiator)) {
initiatorsToAdd.add(initiator);
addInitiators = true;
}
}
boolean removeInitiators = false;
List<String> initiatorsToRemove = new ArrayList<String>();
if (mask.getExistingInitiators() != null && !mask.getExistingInitiators().isEmpty()) {
initiatorsToRemove.addAll(mask.getExistingInitiators());
initiatorsToRemove.removeAll(discoveredInitiators);
removeInitiators = !initiatorsToRemove.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());
removeVolumes = !volumesToRemove.isEmpty();
}
builder.append(String.format("XM refresh: %s initiators; add:{%s} remove:{%s}%n", maskName,
Joiner.on(',').join(initiatorsToAdd), Joiner.on(',').join(initiatorsToRemove)));
builder.append(String.format("XM refresh: %s volumes; add:{%s} remove:{%s}%n", maskName, 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(initiatorsToRemove);
mask.addToExistingInitiatorsIfAbsent(initiatorsToAdd);
mask.removeFromExistingVolumes(volumesToRemove);
mask.addToExistingVolumesIfAbsent(volumesToAdd);
ExportMaskUtils.sanitizeExportMaskContainers(dbClient, mask);
dbClient.updateObject(mask);
} else {
builder.append("XM refresh: There are no changes to the mask\n");
}
_networkDeviceController.refreshZoningMap(mask, initiatorsToRemove, Collections.EMPTY_LIST,
(addInitiators || removeInitiators), true);
log.info(builder.toString());
}
} catch (Exception e) {
log.error("Error when attempting to query HostStorageDomain information", e);
throw HDSException.exceptions.refreshExistingMaskFailure(mask.getLabel());
}
return mask;
}
/**
* Return the initiators from HSD passed in.
*
* @param hsd
* @return
*/
private List<String> getInitiatorsFromHSD(HostStorageDomain hsd) {
List<String> initiatorsList = new ArrayList<String>();
if (null != hsd.getWwnList()) {
for (WorldWideName wwn : hsd.getWwnList()) {
String wwnName = wwn.getWwn();
String normalizedPortWWN = wwnName.replace(HDSConstants.DOT_OPERATOR, "");
initiatorsList.add(normalizedPortWWN);
}
}
if (null != hsd.getIscsiList()) {
for (ISCSIName iscsi : hsd.getIscsiList()) {
initiatorsList.add(iscsi.getiSCSIName());
}
}
return initiatorsList;
}
/**
* Return a map[deviceId] => lun from the give HostStorageDomain.
*
* @param hsd
* @return
*/
private Map<String, Integer> getVolumesFromHSD(HostStorageDomain hsd, StorageSystem storage) {
Map<String, Integer> volumesFromHSD = new HashMap<String, Integer>();
List<Path> pathList = hsd.getPathList();
if (null != pathList) {
for (Path path : pathList) {
String volumeWWN = HDSUtils.generateHitachiVolumeWWN(storage, path.getDevNum());
volumesFromHSD.put(volumeWWN, Integer.valueOf(path.getLun()));
}
}
return volumesFromHSD;
}
/**
* Generates the HiCommand Server URI.
*
* @param system
* @return
*/
private URI getHDSServerManagementServerInfo(StorageSystem system) {
String protocol;
String providerIP;
int port;
if (Boolean.TRUE.equals(system.getSmisUseSSL())) {
protocol = HDSConstants.HTTPS_URL;
} else {
protocol = HDSConstants.HTTP_URL;
}
providerIP = system.getSmisProviderIP();
port = system.getSmisPortNumber();
URI uri = URI.create(String.format("%1$s://%2$s:%3$d/service/StorageManager",
protocol, providerIP, port));
log.info("HiCommand DM server url to query: {}", uri);
return uri;
}
@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);
HDSApiClient hdsApiClient = hdsApiFactory.getClient(
HDSUtils.getHDSServerManagementServerInfo(storage),
storage.getSmisUserName(), storage.getSmisPassword());
String systemObjectID = HDSUtils.getSystemObjectID(storage);
for (Volume volume : volumes) {
String luObjectId = HDSUtils.getLogicalUnitObjectId(volume.getNativeId(), storage);
LogicalUnit logicalUnit = hdsApiClient.getLogicalUnitInfo(systemObjectID, luObjectId);
if (null != logicalUnit && null != logicalUnit.getLdevList() && !logicalUnit.getLdevList().isEmpty()) {
Iterator<LDEV> ldevItr = logicalUnit.getLdevList().iterator();
if (ldevItr.hasNext()) {
LDEV ldev = ldevItr.next();
hdsApiClient.getLogicalUnitInfo(systemObjectID, luObjectId);
String tieringPolicyName = ControllerUtils.getAutoTieringPolicyName(volume.getId(), dbClient);
String policId = HitachiTieringPolicy.getPolicy(
tieringPolicyName.replaceAll(HDSConstants.SLASH_OPERATOR,
HDSConstants.UNDERSCORE_OPERATOR))
.getKey();
String asyncMessageId = hdsApiClient.modifyThinVolumeTieringPolicy(systemObjectID, luObjectId,
ldev.getObjectID(), policId, storage.getModel());
if (null != asyncMessageId) {
HDSJob modifyHDSJob = new HDSModifyVolumeJob(asyncMessageId, volume.getStorageController(),
taskCompleter, HDSModifyVolumeJob.VOLUME_VPOOL_CHANGE_JOB);
ControllerServiceImpl.enqueueJob(new QueueJob(modifyHDSJob));
}
}
}
}
} 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("{} {} updateAutoTieringPolicy END...", storage.getSerialNumber(), message);
}
@Override
public Map<URI, Integer> getExportMaskHLUs(StorageSystem storage, ExportMask exportMask) {
return Collections.emptyMap();
}
@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();
}
}