/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.plugins; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject.CompatibilityStatus; import com.emc.storageos.db.client.model.DiscoveredDataObject.DiscoveryStatus; import com.emc.storageos.db.client.model.Host; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.StorageHADomain; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePool.PoolServiceType; import com.emc.storageos.db.client.model.StoragePool.SupportedDriveTypeValues; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StoragePort.OperationalStatus; import com.emc.storageos.db.client.model.StoragePort.PortType; import com.emc.storageos.db.client.model.StorageProvider; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.plugins.AccessProfile; import com.emc.storageos.plugins.BaseCollectionException; import com.emc.storageos.plugins.StorageSystemViewObject; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.util.VersionChecker; import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator; import com.emc.storageos.volumecontroller.impl.StoragePortAssociationHelper; import com.emc.storageos.volumecontroller.impl.plugins.metering.xtremio.XtremIOMetricsCollector; import com.emc.storageos.volumecontroller.impl.utils.DiscoveryUtils; import com.emc.storageos.volumecontroller.impl.xtremio.XtremIOArrayAffinityDiscoverer; import com.emc.storageos.volumecontroller.impl.xtremio.XtremIOUnManagedVolumeDiscoverer; import com.emc.storageos.volumecontroller.impl.xtremio.prov.utils.XtremIOProvUtils; import com.emc.storageos.xtremio.restapi.XtremIOClient; import com.emc.storageos.xtremio.restapi.XtremIOClientFactory; import com.emc.storageos.xtremio.restapi.XtremIOConstants; import com.emc.storageos.xtremio.restapi.errorhandling.XtremIOApiException; import com.emc.storageos.xtremio.restapi.model.response.XtremIOInitiator; import com.emc.storageos.xtremio.restapi.model.response.XtremIOPort; import com.emc.storageos.xtremio.restapi.model.response.XtremIOSystem; public class XtremIOCommunicationInterface extends ExtendedCommunicationInterfaceImpl { private static final Logger _logger = LoggerFactory .getLogger(XtremIOCommunicationInterface.class); private static final String UP = "up"; private static final String NEW = "new"; private static final String EXISTING = "existing"; private XtremIOClientFactory xtremioRestClientFactory = null; private XtremIOUnManagedVolumeDiscoverer unManagedVolumeDiscoverer; private XtremIOArrayAffinityDiscoverer arrayAffinityDiscoverer; private XtremIOMetricsCollector metricsCollector; public void setXtremioRestClientFactory( XtremIOClientFactory xtremioRestClientFactory) { this.xtremioRestClientFactory = xtremioRestClientFactory; } public void setUnManagedVolumeDiscoverer(XtremIOUnManagedVolumeDiscoverer unManagedVolumeDiscoverer) { this.unManagedVolumeDiscoverer = unManagedVolumeDiscoverer; } public void setArrayAffinityDiscoverer(XtremIOArrayAffinityDiscoverer arrayAffinityDiscoverer) { this.arrayAffinityDiscoverer = arrayAffinityDiscoverer; } public void setMetricsCollector(XtremIOMetricsCollector metricsCollector) { this.metricsCollector = metricsCollector; } @Override public void collectStatisticsInformation(AccessProfile accessProfile) throws BaseCollectionException { _logger.info("Start collecting statistics for IP address {}", accessProfile.getIpAddress()); try { StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, accessProfile.getSystemId()); metricsCollector.collectMetrics(storageSystem, _dbClient); } catch (Exception ex) { _logger.error("Error collecting statistics", ex); throw XtremIOApiException.exceptions.meteringFailed(accessProfile.getIpAddress(), ex.getMessage()); } _logger.info("End collecting statistics for IP address {}", accessProfile.getIpAddress()); } @Override public void scan(AccessProfile accessProfile) throws BaseCollectionException { _logger.info("Scanning started for provider: {}", accessProfile.getSystemId()); StorageProvider.ConnectionStatus cxnStatus = StorageProvider.ConnectionStatus.CONNECTED; StorageProvider provider = _dbClient.queryObject(StorageProvider.class, accessProfile.getSystemId()); XtremIOClient xtremIOClient = null; try { xtremIOClient = (XtremIOClient) xtremioRestClientFactory.getXtremIOV1Client( URI.create(XtremIOConstants.getXIOBaseURI(accessProfile.getIpAddress(), accessProfile.getPortNumber())), accessProfile.getUserName(), accessProfile.getPassword(), true); String xmsVersion = xtremIOClient.getXtremIOXMSVersion(); String minimumSupportedVersion = VersionChecker .getMinimumSupportedVersion(StorageSystem.Type.xtremio).replace("-", "."); String compatibility = (VersionChecker.verifyVersionDetails(minimumSupportedVersion, xmsVersion) < 0) ? StorageSystem.CompatibilityStatus.INCOMPATIBLE.name() : StorageSystem.CompatibilityStatus.COMPATIBLE.name(); provider.setCompatibilityStatus(compatibility); provider.setVersionString(xmsVersion); String systemType = StorageSystem.Type.xtremio.name(); List<XtremIOSystem> xioSystems = xtremIOClient.getXtremIOSystemInfo(); _logger.info("Found {} clusters during scan of XMS {}", xioSystems.size(), accessProfile.getIpAddress()); Map<String, StorageSystemViewObject> storageSystemsCache = accessProfile.getCache(); for (XtremIOSystem system : xioSystems) { String arrayNativeGUID = NativeGUIDGenerator.generateNativeGuid(DiscoveredDataObject.Type.xtremio.name(), system.getSerialNumber()); StorageSystemViewObject viewObject = storageSystemsCache.get(arrayNativeGUID); if (viewObject == null) { viewObject = new StorageSystemViewObject(); } viewObject.setDeviceType(systemType); viewObject.addprovider(accessProfile.getSystemId().toString()); viewObject.setProperty(StorageSystemViewObject.SERIAL_NUMBER, system.getSerialNumber()); viewObject.setProperty(StorageSystemViewObject.VERSION, system.getVersion()); viewObject.setProperty(StorageSystemViewObject.STORAGE_NAME, arrayNativeGUID); storageSystemsCache.put(arrayNativeGUID, viewObject); } } catch (Exception ex) { _logger.error("Error scanning XMS", ex); cxnStatus = StorageProvider.ConnectionStatus.NOTCONNECTED; // throw exception only if system discovery failed. throw XtremIOApiException.exceptions.discoveryFailed(provider.toString()); } finally { provider.setConnectionStatus(cxnStatus.name()); _dbClient.persistObject(provider); if (xtremIOClient != null) { xtremIOClient.close(); } _logger.info("Completed scan of XtremIO StorageProvider. IP={}", accessProfile.getIpAddress()); } } @Override public void discover(AccessProfile accessProfile) throws BaseCollectionException { _logger.info("Entered XtremIO {} -->{}", accessProfile.toString()); if (null != accessProfile.getnamespace() && (accessProfile.getnamespace().equals(StorageSystem.Discovery_Namespaces.UNMANAGED_VOLUMES.toString()))) { discoverUnManagedVolumes(accessProfile); } else { StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, accessProfile.getSystemId()); XtremIOClient xtremIOClient = (XtremIOClient) xtremioRestClientFactory.getRESTClient( URI.create(XtremIOConstants.getXIOBaseURI(accessProfile.getIpAddress(), accessProfile.getPortNumber())), accessProfile.getUserName(), accessProfile.getPassword(), XtremIOProvUtils.getXtremIOVersion(_dbClient, storageSystem), true); _logger.info("Discovery started for system {}", accessProfile.getSystemId()); discoverXtremIOSystem(xtremIOClient, storageSystem); } } public void discoverUnManagedVolumes(AccessProfile accessProfile) { StorageSystem storageSystem = null; String detailedStatusMessage = null; try { storageSystem = _dbClient.queryObject(StorageSystem.class, accessProfile.getSystemId()); if (null == storageSystem) { return; } storageSystem.setDiscoveryStatus(DiscoveredDataObject.DataCollectionJobStatus.IN_PROGRESS.toString()); _dbClient.persistObject(storageSystem); if (accessProfile.getnamespace().equals(StorageSystem.Discovery_Namespaces.UNMANAGED_VOLUMES.toString())) { unManagedVolumeDiscoverer.discoverUnManagedObjects(accessProfile, _dbClient, _partitionManager); } // discovery succeeds detailedStatusMessage = String.format("UnManaged Volumes Discovery completed successfully for XtremIO: %s", storageSystem.getId().toString()); _logger.info(detailedStatusMessage); } catch (Exception e) { detailedStatusMessage = String.format("Discovery of unmanaged volumes failed for system %s because %s", storageSystem .getId().toString(), e.getLocalizedMessage()); _logger.error(detailedStatusMessage, e); throw XtremIOApiException.exceptions.discoveryFailed(storageSystem.getId().toString()); } finally { if (storageSystem != null) { try { // set detailed message storageSystem.setLastDiscoveryStatusMessage(detailedStatusMessage); _dbClient.persistObject(storageSystem); } catch (Exception ex) { _logger.error("Error while updating unmanaged volume discovery status for system.", ex); } } } } private void discoverXtremIOSystem(XtremIOClient restClient, StorageSystem systemInDB) { try { List<StoragePool> pools = new ArrayList<StoragePool>(); XtremIOSystem clusterObject = restClient.getClusterDetails(systemInDB.getSerialNumber()); updateStorageSystemAndPools(clusterObject, systemInDB, pools); Map<String, List<StoragePort>> portMap = discoverPorts(restClient, systemInDB); List<StoragePort> allPorts = new ArrayList<StoragePort>(); allPorts.addAll(portMap.get(NEW)); allPorts.addAll(portMap.get(EXISTING)); List<StoragePort> notVisiblePorts = DiscoveryUtils.checkStoragePortsNotVisible( allPorts, _dbClient, systemInDB.getId()); List<StoragePort> allExistingPorts = new ArrayList<StoragePort>(portMap.get(EXISTING)); if (notVisiblePorts != null && !notVisiblePorts.isEmpty()) { allExistingPorts.addAll(notVisiblePorts); } StoragePortAssociationHelper.runUpdatePortAssociationsProcess(portMap.get(NEW), allExistingPorts, _dbClient, _coordinator, pools); discoverInitiators(restClient, systemInDB); } catch (Exception e) { _logger.error("Error discovering XtremIO cluster", e); // throw exception only if system discovery failed. throw XtremIOApiException.exceptions.discoveryFailed(systemInDB.toString()); } } private void updateStorageSystemAndPools(XtremIOSystem system, StorageSystem systemInDB, List<StoragePool> pools) { StoragePool xioSystemPool = null; if (null != systemInDB) { String firmwareVersion = system.getVersion(); systemInDB.setFirmwareVersion(firmwareVersion); String minimumSupported = VersionChecker .getMinimumSupportedVersion(StorageSystem.Type.xtremio).replace("-", "."); _logger.info("Minimum Supported Version {}", minimumSupported); String compatibility = (VersionChecker.verifyVersionDetails(minimumSupported, firmwareVersion) < 0) ? StorageSystem.CompatibilityStatus.INCOMPATIBLE .name() : StorageSystem.CompatibilityStatus.COMPATIBLE.name(); systemInDB.setCompatibilityStatus(compatibility); systemInDB.setReachableStatus(true); _dbClient.persistObject(systemInDB); } else { throw XtremIOApiException.exceptions.discoveryFailed(system.getSerialNumber()); } try { String poolNativeGUID = NativeGUIDGenerator.generateNativeGuid( systemInDB, system.getSerialNumber(), NativeGUIDGenerator.POOL); _logger.info("Pool Native Guid : {}", poolNativeGUID); @SuppressWarnings("deprecation") List<URI> uriList = _dbClient .queryByConstraint(AlternateIdConstraint.Factory.getStoragePoolByNativeGuidConstraint(poolNativeGUID)); if (uriList.isEmpty()) { xioSystemPool = new StoragePool(); xioSystemPool.setId(URIUtil.createId(StoragePool.class)); xioSystemPool.setNativeGuid(poolNativeGUID); xioSystemPool.setPoolServiceType(PoolServiceType.block.name()); xioSystemPool.setLabel(poolNativeGUID); xioSystemPool.setPoolName(poolNativeGUID); StringSet protocols = new StringSet(); protocols.add("FC"); protocols.add("iSCSI"); xioSystemPool.setProtocols(protocols); StringSet driveTypes = new StringSet(); driveTypes.add(SupportedDriveTypeValues.SSD.toString()); xioSystemPool.addDriveTypes(driveTypes); } else { // TODO : update System details xioSystemPool = _dbClient.queryObject(StoragePool.class, uriList.get(0)); } // fake value set to total capacity xioSystemPool.setMaximumThinVolumeSize(system.getTotalCapacity()); xioSystemPool.setOperationalStatus(StoragePool.PoolOperationalStatus.READY.name()); xioSystemPool.setSupportedResourceTypes(StoragePool.SupportedResourceTypes.THIN_ONLY.name()); xioSystemPool.setPoolServiceType(PoolServiceType.block.name()); xioSystemPool.setFreeCapacity(system.getTotalCapacity() - system.getUsedCapacity()); xioSystemPool.setTotalCapacity(system.getTotalCapacity()); xioSystemPool.setSubscribedCapacity(system.getSubscribedCapacity()); if ((xioSystemPool.getStorageDevice() == null) || !xioSystemPool.getStorageDevice().equals(systemInDB.getId())) { xioSystemPool.setStorageDevice(systemInDB.getId()); } pools.add(xioSystemPool); xioSystemPool.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.toString()); xioSystemPool.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name()); if (uriList.isEmpty()) { _dbClient.createObject(xioSystemPool); } else { _dbClient.persistObject(xioSystemPool); } } catch (Exception e) { _logger.error("Problem while creating/updating XtremIO Storage Pool", e); } } private StorageHADomain createStorageHADomain(StorageSystem system, String scName, int numOfPorts) { StorageHADomain haDomain = null; String haDomainNativeGUID = NativeGUIDGenerator.generateNativeGuid(system, scName, NativeGUIDGenerator.ADAPTER); _logger.info("HA Domain Native Guid : {}", haDomainNativeGUID); @SuppressWarnings("deprecation") List<URI> uriHaList = _dbClient.queryByConstraint(AlternateIdConstraint.Factory .getStorageHADomainByNativeGuidConstraint(haDomainNativeGUID)); if (uriHaList.isEmpty()) { haDomain = new StorageHADomain(); haDomain.setId(URIUtil.createId(StorageHADomain.class)); haDomain.setNativeGuid(haDomainNativeGUID); haDomain.setName(scName); haDomain.setAdapterName(scName); haDomain.setStorageDeviceURI(system.getId()); haDomain.setNumberofPorts(String.valueOf(numOfPorts)); _dbClient.createObject(haDomain); } else { haDomain = _dbClient.queryObject(StorageHADomain.class, uriHaList.get(0)); haDomain.setNumberofPorts(String.valueOf(numOfPorts)); _dbClient.persistObject(haDomain); } return haDomain; } private Map<String, List<StoragePort>> discoverPorts(XtremIOClient restClient, StorageSystem system) throws Exception { Map<String, List<StoragePort>> portMap = new HashMap<String, List<StoragePort>>(); try { String clusterName = restClient.getClusterDetails(system.getSerialNumber()).getName(); List<XtremIOPort> targetPorts = restClient.getXtremIOPortInfo(clusterName); Map<String, List<XtremIOPort>> storageControllerPortMap = new HashMap<String, List<XtremIOPort>>(); for (XtremIOPort targetPort : targetPorts) { String scName = targetPort.getNodeInfo().get(1); List<XtremIOPort> scPorts = storageControllerPortMap.get(scName); if (scPorts == null) { scPorts = new ArrayList<XtremIOPort>(); storageControllerPortMap.put(scName, scPorts); } scPorts.add(targetPort); } portMap.put(NEW, new ArrayList<StoragePort>()); portMap.put(EXISTING, new ArrayList<StoragePort>()); Long portSpeed = 0L; StoragePort port = null; for (String scName : storageControllerPortMap.keySet()) { List<XtremIOPort> scPorts = storageControllerPortMap.get(scName); StorageHADomain haDomain = createStorageHADomain(system, scName, scPorts.size()); for (XtremIOPort targetPort : scPorts) { if (targetPort.getPortSpeed() != null) { String portSpeedStr = targetPort.getPortSpeed().split("G")[0]; try { portSpeed = Long.parseLong(portSpeedStr); } catch (NumberFormatException nfe) { portSpeed = 0L; } } String nativeGuid = NativeGUIDGenerator.generateNativeGuid(system, targetPort.getPortAddress(), NativeGUIDGenerator.PORT); _logger.info("Speed, Target Port Native Guid {} {}", portSpeed, nativeGuid); @SuppressWarnings("deprecation") List<URI> uriList = _dbClient.queryByConstraint(AlternateIdConstraint.Factory .getStoragePortByNativeGuidConstraint(nativeGuid)); if (uriList.isEmpty()) { _logger.info("Creating a Target Port {}", nativeGuid); port = new StoragePort(); port.setId(URIUtil.createId(StoragePort.class)); port.setNativeGuid(nativeGuid); port.setPortSpeed(portSpeed); port.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.toString()); port.setPortType(PortType.frontend.toString()); if ("iscsi".equalsIgnoreCase(targetPort.getPortType().toLowerCase())) { port.setTransportType(StoragePort.TransportType.IP.toString()); port.setPortNetworkId(targetPort.getPortAddress().toLowerCase()); } else { port.setTransportType(targetPort.getPortType().toUpperCase()); // to make it uniform across other arrays port.setPortNetworkId(targetPort.getPortAddress().toUpperCase()); } port.setStorageDevice(system.getId()); port.setPortName(targetPort.getName()); port.setLabel(nativeGuid); port.setOperationalStatus(getOperationalStatus(targetPort).toString()); port.setPortGroup(haDomain.getAdapterName()); port.setStorageHADomain(haDomain.getId()); port.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name()); portMap.get(NEW).add(port); _dbClient.createObject(port); } else { _logger.info("Updating a Target Port {}", nativeGuid); port = _dbClient.queryObject(StoragePort.class, uriList.get(0)); port.setPortSpeed(portSpeed); port.setPortName(targetPort.getName()); port.setLabel(nativeGuid); port.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.toString()); port.setOperationalStatus(getOperationalStatus(targetPort).toString()); // Prior to release-2.4, we only had one default StorageHADomain for XIO array. // During re-discovery when new StorageHADomains are created, update that info on storage ports. port.setPortGroup(haDomain.getAdapterName()); port.setStorageHADomain(haDomain.getId()); port.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name()); portMap.get(EXISTING).add(port); _dbClient.persistObject(port); } } } } catch (Exception e) { _logger.error("Discovering XtremIO target ports failed", e); throw e; } return portMap; } private void discoverInitiators(XtremIOClient restClient, StorageSystem system) throws Exception { try { String clusterName = restClient.getClusterDetails(system.getSerialNumber()).getName(); List<XtremIOInitiator> initiators = restClient.getXtremIOInitiatorsInfo(clusterName); for (XtremIOInitiator initiator : initiators) { @SuppressWarnings("deprecation") List<URI> initiatorUris = _dbClient .queryByConstraint(AlternateIdConstraint.Factory.getInitiatorPortInitiatorConstraint(initiator.getPortAddress())); if (initiatorUris.isEmpty()) { continue; } else { Initiator initiatorObj = _dbClient.queryObject(Initiator.class, initiatorUris.get(0)); initiatorObj.setLabel(initiator.getName()); initiatorObj.mapInitiatorName(system.getSerialNumber(), initiator.getName()); _dbClient.updateObject(initiatorObj); } } } catch (Exception e) { _logger.error("Discovering XtremIO Initiator ports failed", e); throw e; } } private OperationalStatus getOperationalStatus(XtremIOPort targetPort) { if (UP.equalsIgnoreCase(targetPort.getOperationalStatus())) { return OperationalStatus.OK; } else { return OperationalStatus.NOT_OK; } } @Override public void discoverArrayAffinity(AccessProfile accessProfile) throws BaseCollectionException { _logger.info("XtremIO Array Affinity discovery started for : {}", accessProfile.toString()); boolean errorOccurred = false; StringBuilder errorStrBldr = new StringBuilder(); try { XtremIOClient xtremIOClient = (XtremIOClient) xtremioRestClientFactory.getXtremIOV1Client( URI.create(XtremIOConstants.getXIOBaseURI(accessProfile.getIpAddress(), accessProfile.getPortNumber())), accessProfile.getUserName(), accessProfile.getPassword(), true); List<XtremIOSystem> xioSystems = xtremIOClient.getXtremIOSystemInfo(); _logger.info("Found {} clusters for XMS {}", xioSystems.size(), accessProfile.getIpAddress()); for (XtremIOSystem xioSystem : xioSystems) { try { String sysNativeGuid = NativeGUIDGenerator.generateNativeGuid(DiscoveredDataObject.Type.xtremio.name(), xioSystem.getSerialNumber()); // check if system registered in ViPR List<StorageSystem> systems = CustomQueryUtility.getActiveStorageSystemByNativeGuid(_dbClient, sysNativeGuid); if (systems.isEmpty()) { _logger.info("No Storage System found in database for {}, hence skipping..", sysNativeGuid); continue; } StorageSystem system = systems.get(0); // Host based array affinity discovery if (accessProfile.getProps() != null && accessProfile.getProps().get(Constants.HOST_IDS) != null) { String hostIdsStr = accessProfile.getProps().get(Constants.HOST_IDS); _logger.info("Array Affinity Discovery started for Hosts {}, for XtremIO system {}", hostIdsStr, system.getNativeGuid()); String[] hostIds = hostIdsStr.split(Constants.ID_DELIMITER); for (String hostId : hostIds) { _logger.info("Processing Host {}", hostId); Host host = _dbClient.queryObject(Host.class, URI.create(hostId)); if (host != null && !host.getInactive()) { arrayAffinityDiscoverer.findAndUpdatePreferredPoolsForHost(system, host, _dbClient); } } } else { // Storage system based array affinity discovery _logger.info("Array Affinity Discovery started for XtremIO system {}", system.getNativeGuid()); arrayAffinityDiscoverer.findAndUpdatePreferredPools(system, _dbClient, _partitionManager); } } catch (Exception ex) { String errMsg = String.format("Error discovering Array Affinity for XtremIO system %s. Reason: %s", xioSystem.getSerialNumber(), ex.getMessage()); _logger.error(errMsg, ex); errorOccurred = true; errorStrBldr.append(errMsg); } } } catch (Exception e) { _logger.error("Error discovering Array Affinity for XtremIO Provider {}", accessProfile.getIpAddress(), e); throw XtremIOApiException.exceptions.discoveryFailed(accessProfile.getIpAddress()); } finally { if (errorOccurred) { _logger.error("Array Affinity discovery for XtremIO Provider {} failed. {}", accessProfile.getIpAddress(), errorStrBldr.toString()); throw XtremIOApiException.exceptions.discoveryFailed(accessProfile.getIpAddress()); } } _logger.info("XtremIO Array Affinity discovery ended"); } }