/* * Copyright (c) 2008-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.plugins; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.cinder.CinderConstants; import com.emc.storageos.cinder.CinderEndPointInfo; import com.emc.storageos.cinder.api.CinderApi; import com.emc.storageos.cinder.api.CinderApiFactory; import com.emc.storageos.cinder.model.VolumeTypes; 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.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.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.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.StorageProtocol; import com.emc.storageos.db.client.model.StorageProvider; import com.emc.storageos.db.client.model.StorageProvider.ConnectionStatus; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.plugins.AccessProfile; import com.emc.storageos.plugins.BaseCollectionException; import com.emc.storageos.plugins.StorageSystemViewObject; import com.emc.storageos.svcs.errorhandling.resources.ServiceCode; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator; import com.emc.storageos.volumecontroller.impl.StoragePoolAssociationHelper; import com.emc.storageos.volumecontroller.impl.StoragePortAssociationHelper; import com.emc.storageos.volumecontroller.impl.cinder.CinderColletionException; import com.emc.storageos.volumecontroller.impl.cinder.CinderUtils; import com.emc.storageos.volumecontroller.impl.utils.DiscoveryUtils; import com.emc.storageos.volumecontroller.impl.utils.ImplicitPoolMatcher; import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; class Section { int index; String title; String volume_driver; String volume_backend_name; Section() { this.title = ""; this.volume_driver = ""; this.volume_backend_name = ""; } } /** * CinderCommunicationInterface class is an implementation of * CommunicationInterface which is responsible to scan the OpenStack device manager. * It also does the discovery of pools (but not ports and adapters). * */ public class CinderCommunicationInterface extends ExtendedCommunicationInterfaceImpl { /** * Logger instance to log messages. */ private static final Logger _logger = LoggerFactory.getLogger(CinderCommunicationInterface.class); private static final String CONFFILE = "/etc/cinder/cinder.conf"; private static final String VOLUME_BACKEND_NAME = "volume_backend_name"; private static final String VIPR_THICK_POOL = "vipr:is_thick_pool"; private static final long DEFAULT_STORAGE_POOL_SIZE = ControllerUtils.convertBytesToKBytes("10995116277760"); //10 TB in Kilo Bytes static final Integer timeout = 10000; // in milliseconds static final Integer connectTimeout = 10000; // in milliseconds private CinderApiFactory _cinderApiFactory; private CinderEndPointInfo endPointInfo = null; public void setCinderApiFactory(CinderApiFactory cinderApiFactory) { _cinderApiFactory = cinderApiFactory; } @Override public void collectStatisticsInformation(AccessProfile accessProfile) throws BaseCollectionException { // Do Nothing - It will be implemented later _logger.debug("Entering {}", Thread.currentThread().getStackTrace()[1].getMethodName()); _logger.debug("Exiting {}", Thread.currentThread().getStackTrace()[1].getMethodName()); } /** * Process the cinder.conf file and deduce storage systems from it */ @Override public void scan(AccessProfile accessProfile) throws BaseCollectionException { _logger.info("Scanning started for provider: {}", accessProfile.getSystemId()); StorageProvider storageProvider = _dbClient.queryObject(StorageProvider.class, accessProfile.getSystemId()); String username = storageProvider.getUserName(); String password = storageProvider.getPassword(); String hostName = storageProvider.getIPAddress(); // map to hold cinder end point info StringMap providerKeys = storageProvider.getKeys(); if (providerKeys == null) { providerKeys = new StringMap(); } updateKeyInProvider(providerKeys, CinderConstants.KEY_CINDER_HOST_NAME, hostName); Integer portNumber = storageProvider.getPortNumber(); ArrayList<Section> sections = new ArrayList<Section>(); String volume_driver = ""; String auth_strategy = "unknown"; ChannelSftp sftp = null; Session session = null; try { JSch jsch = new JSch(); session = jsch.getSession(username, hostName, portNumber); session.setPassword(password); Hashtable<String, String> config = new Hashtable<String, String>(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); session.connect(timeout); _logger.debug("Session Connected..."); Channel channel = session.openChannel("sftp"); sftp = (ChannelSftp) channel; InputStream ins; sftp.connect(connectTimeout); if (sftp.isConnected()) { _logger.debug("SFTP Connected"); ins = sftp.get(CONFFILE); BufferedReader b = new BufferedReader(new InputStreamReader(ins)); int next_section_index = 0; String section_title = ""; Boolean auth_section = false; while (!sftp.isEOF()) { String line = b.readLine(); if (line == null) { _logger.debug("End of buffer -- break"); break; } if (isComment(line)) { // skip comments continue; } if (isSection(line)) { // start new section section_title = line.substring(line.indexOf('[') + 1, line.indexOf(']')); Section section = new Section(); section.index = next_section_index; section.title = section_title; sections.add(section); next_section_index++; _logger.debug("Section {}: Title: {}", section.index, section.title); auth_section = section_title.startsWith(auth_strategy); continue; } if (!line.contains("=")) { // not a value-parameter pair continue; } // Now process each line String[] splits = line.split("="); if (splits.length == 2) { String parameter = splits[0].trim(); String value = splits[1].trim(); if (auth_section) { if (parameter.equalsIgnoreCase("admin_user") || parameter.equalsIgnoreCase("username")) { updateKeyInProvider(providerKeys, CinderConstants.KEY_CINDER_REST_USER, value); _logger.debug("REST user name = {}", value); } else if (parameter.equalsIgnoreCase("admin_password") || parameter.equalsIgnoreCase("password")) { updateKeyInProvider(providerKeys, CinderConstants.KEY_CINDER_REST_PASSWORD, value); _logger.debug("REST password = {}", value); } else if (parameter.equalsIgnoreCase("admin_tenant_name") || parameter.equalsIgnoreCase("project_name")) { updateKeyInProvider(providerKeys, CinderConstants.KEY_CINDER_TENANT_NAME, value); _logger.debug("Tenant name = {}", value); } else if (parameter.equalsIgnoreCase("auth_uri")) { updateKeyInProvider(providerKeys, CinderConstants.KEY_CINDER_REST_URI_BASE, value); _logger.info("REST uri = {}", value); } } else { // this is a storage section _logger.debug("Storage section: parameter = {}, value = {}", parameter, value); if (parameter.equalsIgnoreCase("auth_strategy")) { auth_strategy = value.trim(); _logger.info("Auth strategy = {}", auth_strategy); } else if (parameter.equalsIgnoreCase("volume_driver")) { volume_driver = value.trim(); sections.get(next_section_index - 1).volume_driver = volume_driver; _logger.debug("Volume driver = {}", volume_driver); } else if (parameter.equalsIgnoreCase("volume_backend_name")) { String volume_backend_name = value.trim(); _logger.debug("Volume backend_name = {}", volume_backend_name); sections.get(next_section_index - 1).volume_backend_name = volume_backend_name; } } } /* if splits.length */ } /* while not EOF */ b.close(); } /* if sftp is connected */ storageProvider.setConnectionStatus(ConnectionStatus.CONNECTED.name()); } /* try */ catch (Exception e) { // exceptionOccured = true; storageProvider.setConnectionStatus(ConnectionStatus.NOTCONNECTED.name()); _logger.error("Exception occurred while scanning provider {}", accessProfile.getSystemId(), e); } finally { fillStorageSystemCache(accessProfile, sections); if (storageProvider.getKeys() == null) { /* first time scan */ storageProvider.setKeys(providerKeys); } _dbClient.persistObject(storageProvider); if (sftp != null) { sftp.disconnect(); } if (session != null) { session.disconnect(); } } _logger.info("Scanning ended for provider: {}", accessProfile.getSystemId()); } private void fillStorageSystemCache(AccessProfile accessProfile, ArrayList<Section> sections) { Map<String, StorageSystemViewObject> storageSystemsCache = accessProfile.getCache(); // Analyze each section, and update the cache if a new backend is found for (Section section : sections) { if (section.volume_driver.isEmpty()) { continue; } String systemType = StorageSystem.Type.openstack.name(); String model = ""; String driverName = section.volume_driver; String label = section.volume_backend_name; if (label.isEmpty()) { // use section title instead label = section.title; } // If it is the default section and label doesn't have Default tag, then add Default tag in label if (section.title.equalsIgnoreCase(CinderConstants.DEFAULT) && !label.toUpperCase().contains(CinderConstants.DEFAULT)) { label = String.format("%s_%s", CinderConstants.DEFAULT, label); } String vendor = ""; String[] driver_splits = section.volume_driver.split("\\."); if (driver_splits.length > 3) { vendor = driver_splits[3]; if (driver_splits.length > 4) { model = driver_splits[4]; } if (driver_splits.length > 5) { driverName = driver_splits[5]; } } // truncate the driver name if it is lengthy, since label length cannot be >64. if (driverName.contains(".")) { driverName = driverName.substring(driverName.lastIndexOf(".")); } StorageSystemViewObject systemVO = null; label = String.format("%s_%s", label, driverName); String serialNumber = generateSerialNumber(label + accessProfile.getIpAddress()); String nativeGuid = String.format("%s+%s", label, serialNumber); if (storageSystemsCache.containsKey(nativeGuid)) { systemVO = storageSystemsCache.get(nativeGuid); _logger.info("Updating existing Storage System: label = {}, model = {},", label, model); _logger.info(" type = {}, serial Number generated = {}", systemType, serialNumber); } else { systemVO = new StorageSystemViewObject(); _logger.info("Adding new Storage System: label = {}, model = {},", label, model); _logger.info(" type = {}, serial Number generated = {}", systemType, serialNumber); } systemVO.setDeviceType(systemType); systemVO.addprovider(accessProfile.getSystemId() .toString()); systemVO.setProperty(StorageSystemViewObject.MODEL, model); systemVO.setProperty(StorageSystemViewObject.SERIAL_NUMBER, serialNumber); systemVO.setProperty(StorageSystemViewObject.STORAGE_NAME, nativeGuid); storageSystemsCache.put(nativeGuid, systemVO); } _logger.info("Found {} system(s) during scanning for ip {}", storageSystemsCache.size(), accessProfile.getIpAddress()); } /** * Generate serial number which will be of 11 digits. * * @param string (label + Driver name + Provider IP address/FQDN) * @return serial number */ private String generateSerialNumber(String str) { int value = str.hashCode(); String serialNumber = null; /** * The String which we generate is of 11 digits. * The first digit is either 0 or 1. Remaining 10 digits are generated from hashCode() * - add 0 at the beginning when value is +ve * - add 1 at the beginning when value is -ve * (to differentiate it from value which would obtain in +ve case) */ if (value < 0) { /** when the generated number is negative, it is an overflown value of int max size(2147483647) */ value = -value; serialNumber = String.format("%010d", value); serialNumber = "1" + serialNumber; } else { serialNumber = String.format("%011d", value); } return serialNumber; } /** * Get volume types, and create a storage pool for each volume type */ @Override public void discover(AccessProfile accessProfile) throws BaseCollectionException { if ((null != accessProfile.getnamespace()) && (accessProfile.getnamespace() .equals(StorageSystem.Discovery_Namespaces.UNMANAGED_VOLUMES .toString()))) { discoverUnManagedVolumes(accessProfile); } else { _logger.info("Discovery started for system {}", accessProfile.getSystemId()); List<StoragePool> newPools = new ArrayList<StoragePool>(); List<StoragePool> updatePools = new ArrayList<StoragePool>(); List<StoragePool> allPools = new ArrayList<StoragePool>(); String token = ""; StorageSystem system = null; StorageProvider provider = null; String detailedStatusMessage = "Unknown Status"; try { String hostName = null; String restuserName = null; String restPassword = null; String restBaseUri = null; String tenantName = null; String oldToken = null; String tenantId = null; system = _dbClient.queryObject(StorageSystem.class, accessProfile.getSystemId()); system.setReachableStatus(true); // first add storage ports if necessary addPorts(system); // now do the pool discovery URI providerUri = system.getActiveProviderURI(); provider = _dbClient.queryObject(StorageProvider.class, providerUri); if (null != provider.getKeys()) { StringMap providerKeys = provider.getKeys(); oldToken = providerKeys.get(CinderConstants.KEY_CINDER_REST_TOKEN); hostName = providerKeys.get(CinderConstants.KEY_CINDER_HOST_NAME); restuserName = providerKeys.get(CinderConstants.KEY_CINDER_REST_USER); restPassword = providerKeys.get(CinderConstants.KEY_CINDER_REST_PASSWORD); restBaseUri = providerKeys.get(CinderConstants.KEY_CINDER_REST_URI_BASE); tenantName = providerKeys.get(CinderConstants.KEY_CINDER_TENANT_NAME); tenantId = providerKeys.get(CinderConstants.KEY_CINDER_TENANT_ID); } if (null == endPointInfo) { endPointInfo = new CinderEndPointInfo(hostName, restuserName, restPassword, tenantName); if (restBaseUri.startsWith(CinderConstants.HTTP_URL)) { endPointInfo.setCinderBaseUriHttp(restBaseUri); } else { endPointInfo.setCinderBaseUriHttps(restBaseUri); } // Always set the token and tenant id, when new instance is created endPointInfo.setCinderToken(oldToken); endPointInfo.setCinderTenantId(tenantId); } CinderApi api = _cinderApiFactory.getApi(providerUri, endPointInfo); _logger.debug("discover : Got the cinder api factory for provider with id: {}", providerUri); // check if the cinder is authenticated and if the token is valid if (null == oldToken || (isTokenExpired(oldToken))) { // This means, authentication is required, go and fetch the token token = api.getAuthToken(restBaseUri + "/tokens"); if (null != token) { _logger.debug("Got new token : {}", token); // update the token in the provider provider.addKey(CinderConstants.KEY_CINDER_REST_TOKEN, token); provider.addKey(CinderConstants.KEY_CINDER_TENANT_ID, endPointInfo.getCinderTenantId()); } } else { token = oldToken; _logger.debug("Using the old token : {}", token); } if (token.length() > 1) { // Now get the number of volume types VolumeTypes types = api.getVolumeTypes(); if (types != null) { _logger.info("Got {} Volume Type(s)", types.volume_types.length); boolean isDefaultStoragePoolCreated = false; for (int i = 0; i < types.volume_types.length; i++) { boolean isNew = false; String poolName = types.volume_types[i].name; String nativeGuid = types.volume_types[i].id; _logger.info("Storage Pool name = {}, id = {}", poolName, nativeGuid); // Now find association with storage system Map<String, String> extra_specs = types.volume_types[i].extra_specs; String system_title = extra_specs.get(VOLUME_BACKEND_NAME); boolean isThickPool = Boolean .parseBoolean(extra_specs.get(VIPR_THICK_POOL)); // If no volume backend name, use default if (system_title == null) { system_title = CinderConstants.DEFAULT; } if (system.getNativeGuid().toUpperCase().contains(system_title.toUpperCase())) { // Check if volume type belongs to the default storage system if (system.getNativeGuid().toUpperCase().startsWith(CinderConstants.DEFAULT)) { isDefaultStoragePoolCreated = true; } // do the association _logger.info("Found association between system {} and pool {}", system_title, poolName); StoragePool pool = checkPoolExistsInDB(nativeGuid); if (null == pool) { isNew = true; pool = createPoolforStorageSystem(system, nativeGuid, poolName, isThickPool); newPools.add(pool); } pool.setPoolName(poolName); pool.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.name()); pool.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name()); if (!isNew) { updatePools.add(pool); } } else { _logger.info("Pool {} doesn't belong to storage system {}", poolName, system.getLabel()); } } /* for */ // If it is a default storage system and volume type is not created in cinder, // create default storage pool for storage system if (system.getNativeGuid().toUpperCase().startsWith(CinderConstants.DEFAULT) && !isDefaultStoragePoolCreated) { _logger.debug("Creating defual pool for default storage system"); String nativeGuid = "DefaultPool"; StoragePool pool = checkPoolExistsInDB(nativeGuid); String poolName = "DefaultPool"; if (null != pool) { updatePools.add(pool); } else { pool = createPoolforStorageSystem(system, nativeGuid, poolName, false); newPools.add(pool); } pool.setPoolName(poolName); pool.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.name()); pool.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name()); } StoragePoolAssociationHelper.setStoragePoolVarrays(system.getId(), newPools, _dbClient); allPools.addAll(newPools); allPools.addAll(updatePools); StringBuffer errorMessage = new StringBuffer(); ImplicitPoolMatcher.matchModifiedStoragePoolsWithAllVpool(allPools, _dbClient, _coordinator, accessProfile.getSystemId(), errorMessage); _logger.info("New pools size: {}", newPools.size()); _logger.info("updatePools size: {}", updatePools.size()); DiscoveryUtils.checkStoragePoolsNotVisible(allPools, _dbClient, system.getId()); _dbClient.createObject(newPools); _dbClient.persistObject(updatePools); // discovery succeeds detailedStatusMessage = String.format("Discovery completed successfully for OpenStack: %s", accessProfile.getSystemId()); } /* if types */ else { _logger.error("Error in getting volume types from cinder"); } } /* if token length */ else { _logger.error("Error in getting token from keystone"); } } catch (Exception e) { if (null != system) { cleanupDiscovery(system); } detailedStatusMessage = String.format( "Discovery failed for Storage System: %s because %s", system.toString(), e.getLocalizedMessage()); _logger.error(detailedStatusMessage, e); throw new CinderColletionException(false, ServiceCode.DISCOVERY_ERROR, null, detailedStatusMessage, null, null); } finally { try { if (system != null) { system.setLastDiscoveryStatusMessage(detailedStatusMessage); _dbClient.persistObject(system); } // persist the provider if (null != provider) { _dbClient.persistObject(provider); } } catch (DatabaseException e) { _logger.error( "Failed to persist cinder storage system to Database, Reason: {}", e.getMessage(), e); } } _logger.info("Discovery Ended for system {}", accessProfile.getSystemId()); } } private StoragePool createPoolforStorageSystem(StorageSystem system, String nativeGuid, String poolName, boolean isThickPool) { _logger.debug("Creating storage pool {} for storage system {}", poolName, system); StoragePool pool = new StoragePool(); pool.setNativeGuid(nativeGuid); pool.setStorageDevice(system.getId()); pool.setId(URIUtil.createId(StoragePool.class)); pool.setNativeId(nativeGuid); pool.setOperationalStatus(StoragePool.PoolOperationalStatus.READY .toString()); pool.setPoolServiceType(PoolServiceType.block.toString()); pool.setRegistrationStatus(DiscoveredDataObject.RegistrationStatus.REGISTERED .toString()); StringSet protocols = new StringSet(); String sys_nativeGuid = system.getNativeGuid().toLowerCase(); if (sys_nativeGuid.contains("iscsi")) { protocols.add("iSCSI"); } if (sys_nativeGuid.contains("fc")) { protocols.add("FC"); } pool.setProtocols(protocols); if (isThickPool) { pool.setSupportedResourceTypes(StoragePool.SupportedResourceTypes.THICK_ONLY .toString()); pool.setMaximumThickVolumeSize(DEFAULT_STORAGE_POOL_SIZE); // 10 TB in Kilo Bytes } else { pool.setSupportedResourceTypes(StoragePool.SupportedResourceTypes.THIN_ONLY .toString()); pool.setMaximumThinVolumeSize(DEFAULT_STORAGE_POOL_SIZE); // 10 TB in Kilo Bytes } // UNSYNC_ASSOC -> snapshot, UNSYNC_UNASSOC -> clone StringSet copyTypes = new StringSet(); copyTypes.add(StoragePool.CopyTypes.UNSYNC_ASSOC.name()); copyTypes.add(StoragePool.CopyTypes.UNSYNC_UNASSOC.name()); pool.setSupportedCopyTypes(copyTypes); pool.setThinVolumePreAllocationSupported(Boolean.FALSE); // TODO workaround to display the display name based on the pool name pool.setLabel(poolName); /* * TODO: Keeping the total capacity as 10 TB as there is no API/CLI * to know the volume type's capacity from cinder. These values * should be updated for actual capacity if in future cinder comes up * a way to know these values. * * Further these values will be adjusted as volume/snapshot gets created/deleted */ pool.setFreeCapacity(DEFAULT_STORAGE_POOL_SIZE); // 10 TB in Kilo Bytes pool.setTotalCapacity(DEFAULT_STORAGE_POOL_SIZE); // 10 TB in Kilo Bytes return pool; } private void discoverUnManagedVolumes(AccessProfile accessProfile) { // Do Nothing - It will be implemented later _logger.info("UnManaged volume discovery is not supported. Storage System: {}", accessProfile.getSystemId()); } /** * Checks if the last token fetched is expired * * The token fetched will be valid for one hour only * * @param oldToken * @return true or false */ private boolean isTokenExpired(String oldToken) { // TODO : Add the implementation to check the token expiry return true; } /** * This method adds storage ports to the system if not done already * * @throws IOException */ private void addPorts(StorageSystem system) throws IOException { StorageProtocol.Transport supportedProtocol = getProtocol(system); _logger.info("Checking if system {} needs a new storage port", system.getLabel()); if (supportedProtocol == null) { return; } _logger.info("System {} supports protocol {}", system.getLabel(), supportedProtocol.name().toString()); boolean isPresent = false; List<StoragePort> allPorts = new ArrayList<StoragePort>(); URIQueryResultList storagePortURIs = new URIQueryResultList(); URI sysid = system.getId(); _dbClient.queryByConstraint( ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(sysid), storagePortURIs); Iterator<URI> storagePortsIter = storagePortURIs.iterator(); while (storagePortsIter.hasNext()) { URI storagePortURI = storagePortsIter.next(); StoragePort storagePort = _dbClient.queryObject(StoragePort.class, storagePortURI); if (storagePort != null && !storagePort.getInactive() && (storagePort.getTransportType().equalsIgnoreCase( supportedProtocol.name().toString()))) { _logger.info("System {} already has port for protocol {}", system.getLabel(), supportedProtocol.name().toString()); isPresent = true; if (storagePort.getPortNetworkId() == null) { // update it -- this needs to change storagePort.setPortNetworkId(storagePort.getNativeGuid()); _dbClient.persistObject(storagePort); } allPorts.add(storagePort); } } // while if (!isPresent) { StorageHADomain adapter = CinderUtils.getStorageAdapter(system, _dbClient); // Now we need to add the port to the system _logger.info("Adding new storage port of type {} to storage system {}", supportedProtocol.name().toString(), system.getLabel()); StoragePort port = new StoragePort(); port.setId(URIUtil.createId(StoragePort.class)); port.setStorageDevice(sysid); // port name is "DEFAULT" since we don't know how to name it String portName = CinderConstants.DEFAULT; String nativeGuid = NativeGUIDGenerator.generateNativeGuid(system, portName, NativeGUIDGenerator.PORT); port.setNativeGuid(nativeGuid); port.setPortNetworkId(nativeGuid); port.setRegistrationStatus(DiscoveredDataObject.RegistrationStatus.REGISTERED .toString()); // always treat it as a frontend port port.setPortType(PortType.frontend.name()); port.setOperationalStatus(OperationalStatus.OK.toString()); port.setTransportType(supportedProtocol.name().toString()); port.setLabel(portName); port.setPortName(portName); port.setStorageHADomain(adapter.getId()); port.setPortGroup(CinderConstants.CINDER_PORT_GROUP); port.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.name()); port.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name()); _dbClient.createObject(port); allPorts.add(port); } StoragePortAssociationHelper.updatePortAssociations(allPorts, _dbClient); DiscoveryUtils.checkStoragePortsNotVisible(allPorts, _dbClient, sysid); } /** * Deduce the supported protocol from the storage system native ID * Note: this method may be prone to error * But we don't have any other reliable API from OpenStack for this */ private StorageProtocol.Transport getProtocol(StorageSystem system) { String nativeGuid = system.getNativeGuid().toLowerCase(); if (nativeGuid.contains("iscsi")) // name contains iscsi { return StorageProtocol.Transport.IP; } if (nativeGuid.contains("fc")) // name contains FC { return StorageProtocol.Transport.FC; } if (nativeGuid.contains("nfs")) // probably a file type -- do not use this { return null; } return StorageProtocol.Transport.IP; // default assume iscsi } /** * If discovery fails, then mark the system as unreachable. The discovery * framework will remove the storage system from the database. * * @param system * the system that failed discovery. */ private void cleanupDiscovery(StorageSystem system) { try { system.setReachableStatus(false); _dbClient.persistObject(system); } catch (DatabaseException e) { _logger.error("discoverStorage failed. Failed to update discovery status to ERROR.", e); } } /** * Check if Pool exists in DB. * * @param poolInstance * @param _dbClient * @param profile * @return * @throws IOException */ protected StoragePool checkPoolExistsInDB(String nativeGuid) throws IOException { StoragePool pool = null; URIQueryResultList queryResult = new URIQueryResultList(); // use NativeGuid to lookup Pools in DB _dbClient.queryByConstraint(AlternateIdConstraint.Factory .getStoragePoolByNativeGuidConstraint(nativeGuid), queryResult); while (queryResult.iterator().hasNext()) { URI poolURI = queryResult.iterator().next(); if (null != poolURI) { StoragePool poolInDB = _dbClient.queryObject(StoragePool.class, poolURI); if (!poolInDB.getInactive()) { pool = poolInDB; break; } } } return pool; } private boolean isComment(String line) { return (line.trim().startsWith("#")); } private boolean isSection(String line) { return (line.trim().startsWith("[")); } private void updateKeyInProvider(StringMap providerKeys, String key, String value) { if (!providerKeys.containsKey(key) || !providerKeys.get(key).equals(value)) { providerKeys.put(key, value); } } }