/* * Copyright (c) 2008-2011 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.processor; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.StorageHADomain; import com.emc.storageos.db.client.model.StorageHADomain.HADomainType; import com.emc.storageos.db.client.model.StoragePool; 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.StorageSystem; import com.emc.storageos.db.client.util.WWNUtility; import com.emc.storageos.plugins.AccessProfile; import com.emc.storageos.plugins.BaseCollectionException; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.plugins.common.domainmodel.Operation; import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator; import com.emc.storageos.volumecontroller.impl.StoragePoolAssociationHelper; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.cim.UnsignedInteger16; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Processor responsible for handling Provider response data and creates Physical StoragePorts */ public class StoragePortProcessor extends StorageProcessor { private Logger _logger = LoggerFactory.getLogger(StoragePortProcessor.class); private static final String PORTNAME = "EMCPortName"; private static final String DEVICEID = "DeviceID"; private static final String SPEED = "Speed"; private static final String SYSTEMNAME = "SystemName"; private static final String LINKTECHNOLOGY = "LinkTechnology"; private static final String USAGERESTRICTION = "UsageRestriction"; private static final String FC = "FC"; private static final String IP = "IP"; private static final String ISCSI = "iSCSI"; private static final String OPERATIONALSTATUS = "OperationalStatus"; private static final String EMC_PORT_ATTRIBUTES = "EMCPortAttributes"; private static final String FRONTEND_PORT = "2"; private static final String BACKEND_PORT = "3"; private static final String LINKTECHNOLOGY_ETHERNET = "2"; private static final String LINKTECHNOLOGY_FC = "4"; private static final UnsignedInteger16 EMC_PORT_ATTRIBUTE_ACLX_FLAG = new UnsignedInteger16(1); private AccessProfile profile = null; private List<StoragePort> _newPortList = null; private List<StoragePort> _updatePortList = null; private DbClient _dbClient; private static final int GB = 1024 * 1024 * 1024; private List<Object> args; private static final UnsignedInteger16 ok_code = new UnsignedInteger16(2); private static final UnsignedInteger16 stopped_code = new UnsignedInteger16(10); /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public void processResult( Operation operation, Object resultObj, Map<String, Object> keyMap) throws BaseCollectionException { try { final Iterator<CIMInstance> it = (Iterator<CIMInstance>) resultObj; profile = (AccessProfile) keyMap.get(Constants.ACCESSPROFILE); Set<String> protocols = (Set<String>) keyMap.get(Constants.PROTOCOLS); _newPortList = new ArrayList<StoragePort>(); _updatePortList = new ArrayList<StoragePort>(); _dbClient = (DbClient) keyMap.get(Constants.dbClient); CoordinatorClient coordinator = (CoordinatorClient) keyMap.get(Constants.COORDINATOR_CLIENT); Map<URI, StoragePool> poolsToMatchWithVpool = (Map<URI, StoragePool>) keyMap.get(Constants.MODIFIED_STORAGEPOOLS); Set<URI> systemsToRunRPConnectivity = (HashSet<URI>) keyMap.get(Constants.SYSTEMS_RUN_RP_CONNECTIVITY); StorageSystem device = _dbClient.queryObject(StorageSystem.class, profile.getSystemId()); CIMObjectPath storageAdapterPath = getObjectPathfromCIMArgument(args); Iterable<String> adapterItr = Splitter.on(Constants.PATH_DELIMITER_PATTERN) .limit(3).split(storageAdapterPath.getKey(Constants.NAME).getValue().toString()); String adapterNativeGuid = NativeGUIDGenerator.generateNativeGuid(device, Iterables.getLast(adapterItr), NativeGUIDGenerator.ADAPTER); StorageHADomain haDomain = getStorageAdapter(_dbClient, adapterNativeGuid); if (null == haDomain) { _logger.info("Adapter Not found"); return; } while (it.hasNext()) { CIMInstance portInstance = null; StoragePort port = null; try { portInstance = it.next(); // skip back end ports other than RDF Ports if (!HADomainType.REMOTE.name().equalsIgnoreCase( haDomain.getAdapterType()) && BACKEND_PORT.equalsIgnoreCase(getCIMPropertyValue(portInstance, USAGERESTRICTION))) { continue; } // only if its an EthernetPort, as protocolEnd point needs // to run only for Ethernet // Ports , because SCSI address we don't have it in // CIM_LogicalPort Class // 2 - Ethernet Port 4 - FC Port if (LINKTECHNOLOGY_ETHERNET.equalsIgnoreCase(getCIMPropertyValue(portInstance, LINKTECHNOLOGY))) { port = createStoragePort(null, portInstance, profile, haDomain, false, IP, device); checkProtocolAlreadyExists(protocols, ISCSI); String deviceId = getCIMPropertyValue(portInstance, DEVICEID); /* * For SMI-S 8.x, While getting the iSCSI Port details, we use SystemName property * (Ex. SYMMETRIX-+-<<SERIAL>>-+-SE-1G-+-0) * Where this call just add the deviceId to the KeyMap (i.e SE-1G-+-0). * Hence manually constructing the key. */ if (device.getUsingSmis80()) { String systemName = getCIMPropertyValue(portInstance, SYSTEM_NAME); StringBuffer deviceIdStrBuf = new StringBuffer(systemName); deviceIdStrBuf.append(Constants.SMIS80_DELIMITER).append(deviceId); deviceId = deviceIdStrBuf.toString(); } _logger.debug("Adding iSCSI Port instance {} to keyMap", deviceId); keyMap.put(deviceId, port); addPath(keyMap, operation.getResult(), portInstance.getObjectPath()); } else if (LINKTECHNOLOGY_FC.equalsIgnoreCase(getCIMPropertyValue(portInstance, LINKTECHNOLOGY))) { port = checkStoragePortExistsInDB(portInstance, device, _dbClient); checkProtocolAlreadyExists(protocols, FC); createStoragePort(port, portInstance, profile, haDomain, true, FC, device); } else { _logger.debug("Unsupported Port : {}", getCIMPropertyValue(portInstance, DEVICEID)); } } catch (Exception e) { _logger.warn("Port Discovery failed for {}", getCIMPropertyValue(portInstance, DEVICEID), e); } } _dbClient.createObject(_newPortList); _dbClient.persistObject(_updatePortList); // ports used later to run Transport Zone connectivity List<List<StoragePort>> portsUsedToRunTZoneConnectivity = (List<List<StoragePort>>) keyMap.get(Constants.STORAGE_PORTS); portsUsedToRunTZoneConnectivity.add(_newPortList); portsUsedToRunTZoneConnectivity.add(_updatePortList); List<StoragePool> modifiedPools = StoragePoolAssociationHelper.getStoragePoolsFromPorts(_dbClient, _newPortList, _updatePortList); for (StoragePool pool : modifiedPools) { // pool matcher will be invoked on this pool if (!poolsToMatchWithVpool.containsKey(pool.getId())) { poolsToMatchWithVpool.put(pool.getId(), pool); } } // Systems used to run RP connectivity later after runing pool matcher systemsToRunRPConnectivity.addAll(StoragePoolAssociationHelper.getStorageSytemsFromPorts(_newPortList, null)); systemsToRunRPConnectivity.addAll(StoragePoolAssociationHelper.getStorageSytemsFromPorts(_updatePortList, null)); // discovered ports used later to check for not visible ports List<StoragePort> discoveredPorts = (List<StoragePort>) keyMap.get(Constants.DISCOVERED_PORTS); discoveredPorts.addAll(_newPortList); discoveredPorts.addAll(_updatePortList); _logger.debug("# Pools used in invoking PoolMatcher during StoragePortProcessor {}", Joiner.on("\t").join(poolsToMatchWithVpool.keySet())); } catch (Exception e) { _logger.error("Port Discovery failed -->{}", getMessage(e)); } finally { _newPortList = null; _updatePortList = null; } } /** * Verify whether protocolType already exists or not. If it doesn't exist * then add. * * @param protocols * @param protocolType */ private void checkProtocolAlreadyExists(Set<String> protocols, String protocolType) { if (!protocols.contains(protocolType)) { protocols.add(protocolType); } } /** * create StoragePort Record, if not present already, else update only the properties. * * @param port * @param portInstance * @throws URISyntaxException * @throws IOException */ private StoragePort createStoragePort( StoragePort port, CIMInstance portInstance, AccessProfile profile, StorageHADomain haDomain, boolean flag, String transportType, StorageSystem device) throws URISyntaxException, IOException { boolean newPort = false; if (null == port) { newPort = true; port = new StoragePort(); port.setId(URIUtil.createId(StoragePort.class)); // if true, then its FC Port or else its Ethernet, PORTID // or ethernet will be updated later in ProtocolEndPoint Processor if (flag) { port.setPortNetworkId(WWNUtility.getWWNWithColons(getCIMPropertyValue(portInstance, PORTID))); } port.setStorageDevice(profile.getSystemId()); String portNativeGuid = NativeGUIDGenerator.generateNativeGuid(_dbClient, port); port.setNativeGuid(portNativeGuid); port.setLabel(portNativeGuid); port.setPortGroup(haDomain.getAdapterName()); port.setStorageHADomain(haDomain.getId()); } setPortType(port, portInstance); port.setTransportType(transportType); port.setPortName(getCIMPropertyValue(portInstance, PORTNAME)); port.setCompatibilityStatus(DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name()); port.setDiscoveryStatus(DiscoveredDataObject.DiscoveryStatus.VISIBLE.name()); UnsignedInteger16[] operationalStatusCodes = (UnsignedInteger16[]) portInstance.getPropertyValue(OPERATIONALSTATUS); OperationalStatus operationalStatus = getPortOperationalStatus(operationalStatusCodes); if (OperationalStatus.NOT_OK.equals(operationalStatus)) { _logger.info("StoragePort {} operationalStatus is NOT_OK. operationalStatusCodes collected from SMI-S :{}" , port.getId(), operationalStatusCodes); } else { _logger.debug("operationalStatusCodes :{}", operationalStatusCodes); // there can be multiple statuses. {OK, Online}, {OK, Stopped} if (operationalStatusCodes != null && operationalStatusCodes.length > 1 && Arrays.asList(operationalStatusCodes).contains(stopped_code)) { _logger.info("StoragePort {} operational status is {OK, Stopped}. operationalStatusCodes :{}", port.getId(), operationalStatusCodes); } } port.setOperationalStatus(operationalStatus.name()); String portSpeed = getCIMPropertyValue(portInstance, SPEED); if (null != portSpeed) { // SMI returns port speed in bits per sec ?? Is this always true? Long portSpeedInBitsPerSec = Long.parseLong(portSpeed); Long portSpeedInGbps = portSpeedInBitsPerSec / GB; port.setPortSpeed(portSpeedInGbps); } if (FRONTEND_PORT.equalsIgnoreCase(getCIMPropertyValue(portInstance, USAGERESTRICTION))) { setCompatibilityByACLXFlag(device, portInstance, port); } if (flag) { if (newPort) { _logger.info("Creating port - {}:{}", port.getLabel(), port.getNativeGuid()); _newPortList.add(port); } else { _logger.info("Updating port - {}:{}", port.getLabel(), port.getNativeGuid()); _updatePortList.add(port); } } ; return port; } private void setCompatibilityByACLXFlag(StorageSystem storageSystem, CIMInstance portInstance, StoragePort port) { Object portAttributesValue = portInstance.getPropertyValue(EMC_PORT_ATTRIBUTES); if (portAttributesValue != null && storageSystem.checkIfVmax3()) { boolean foundACLXFlag = false; UnsignedInteger16[] portAttributes = (UnsignedInteger16[]) portAttributesValue; for (UnsignedInteger16 portAttribute : portAttributes) { if (portAttribute.equals(EMC_PORT_ATTRIBUTE_ACLX_FLAG)) { foundACLXFlag = true; break; } } // VMAX GIGE ports do not report EMC_PORT_ATTRIBUTE_ACLX_FLAG // If there is Virtual ProtocolEndpoint associated to this physical port, consider masking is enabled... if (LINKTECHNOLOGY_ETHERNET.equalsIgnoreCase(getCIMPropertyValue(portInstance, LINKTECHNOLOGY))) { foundACLXFlag = true; } String compatibilityStatus = (foundACLXFlag) ? DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name() : DiscoveredDataObject.CompatibilityStatus.INCOMPATIBLE.name(); _logger.info(String.format("setCompatibilityByACLXFlag(%s) = %s", port.getNativeGuid(), compatibilityStatus)); port.setCompatibilityStatus(compatibilityStatus); } } private void setPortType(StoragePort port, CIMInstance portInstance) { if (FRONTEND_PORT.equalsIgnoreCase(getCIMPropertyValue(portInstance, USAGERESTRICTION))) { port.setPortType(PortType.frontend.toString()); } else if (BACKEND_PORT.equalsIgnoreCase(getCIMPropertyValue(portInstance, USAGERESTRICTION))) { port.setPortType(PortType.rdf.toString()); } else { port.setPortType(PortType.Unknown.toString()); } } /** * Returns operationalStatus based on the given operationalCodes collected from smisProvider. * OpertaionalCode 2 means Ok. * ValueMap("0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, .., 0x8000.."), * Values( * "Unknown, Other, OK, Degraded, Stressed, Predictive Failure, Error, Non-Recoverable Error, Starting, Stopping, Stopped, In Service, No Contact, Lost Communication, Aborted, Dormant, Supporting Entity in Error, Completed, Power Mode, Relocating, DMTF Reserved, Vendor Reserved" * )] * * @param operationalCodes * @return */ public static OperationalStatus getPortOperationalStatus(UnsignedInteger16[] operationalCodes) { OperationalStatus result = StoragePort.OperationalStatus.NOT_OK; // operationalStatusCodes may have multiple statuses ({OK, Online}, {OK, Stopped}, {Stopped, OK}) if (operationalCodes != null && Arrays.asList(operationalCodes).contains(ok_code)) { result = StoragePort.OperationalStatus.OK; } return result; } private void setStorageAdapterReference(StoragePort port, CIMInstance portInstance, StorageSystem device) { String adapterName = null; try { adapterName = portInstance.getObjectPath().getKey(SYSTEMNAME) .getValue().toString(); Iterable<String> adapterItr = Splitter.on(Constants.PATH_DELIMITER_PATTERN) .limit(3).split(adapterName); URIQueryResultList result = new URIQueryResultList(); _dbClient .queryByConstraint( AlternateIdConstraint.Factory .getStorageHADomainByNativeGuidConstraint(NativeGUIDGenerator .generateNativeGuid(device, Iterables.getLast(adapterItr), NativeGUIDGenerator.ADAPTER)), result); if (result.iterator().hasNext()) { URI portGroup = result.iterator().next(); port.setStorageHADomain(portGroup); StorageHADomain haDomain = _dbClient.queryObject( StorageHADomain.class, portGroup); port.setPortGroup(haDomain.getAdapterName()); } } catch (Exception e) { _logger.warn("Storage Port not found : {}", adapterName); } } @Override protected void setPrerequisiteObjects(List<Object> inputArgs) throws BaseCollectionException { args = inputArgs; } }