/* * Copyright (c) 2008-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.plugins.metering.smis.processor; import java.net.URI; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.cim.CIMArgument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.StorageHADomain; import com.emc.storageos.db.client.model.StorageSystem; 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.ControllerUtils; public class FEAdaptStatsProcessor extends CommonStatsProcessor { private final Logger _logger = LoggerFactory.getLogger(FEAdaptStatsProcessor.class); private final static Long VNX_CLOCK_TICK_INTERVAL_MSEC = 100L; private PortMetricsProcessor portMetricsProcessor; public static enum FEAdaptMetric { UnKnown, InstanceID, ElementType, TotalIOs, IOTimeCounter, IdleTimeCounter, EMCCollectionTimeDir, EMCIdleTimeDir, StatisticTime, EMCPercentBusyTime; private static final FEAdaptMetric[] metricCopyOfValues = values(); public static FEAdaptMetric lookup(String name) { for (FEAdaptMetric value : metricCopyOfValues) { if (value.name().equals(name)) { return value; } } return UnKnown; } } @Override public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap) throws BaseCollectionException { try { CIMArgument<?>[] outputArguments = (CIMArgument<?>[]) resultObj; DbClient dbClient = (DbClient) keyMap.get(Constants.dbClient); AccessProfile profile = (AccessProfile) keyMap.get(Constants.ACCESSPROFILE); StorageSystem system = dbClient.queryObject(StorageSystem.class, profile.getSystemId()); List<String> metricSequence = (List<String>) keyMap.get(Constants.STORAGEOS_FEADAPT_MANIFEST); String[] feadaptMetricValues = ((String[]) outputArguments[0].getValue())[0].split("\n"); Map<String, StorageHADomain> haDomains = getHADomainOfSystem(dbClient, profile.getSystemId()); if (null == metricSequence || metricSequence.isEmpty()) { _logger.error("No metric sequence for FEAdaptStatsProcessor; no processing will happen"); return; } for (String metricValue : feadaptMetricValues) { if (metricValue.isEmpty()) { continue; } String metrics[] = metricValue.split(Constants.SEMI_COLON); String instanceId = metrics[0]; String instanceName; if (instanceId.contains(Constants.SMIS80_DELIMITER)) { instanceName = instanceId.replaceAll(".*\\Q" + Constants.SMIS80_DELIMITER + "\\E", ""); } else { instanceName = instanceId.replaceAll(".*\\+", ""); } StorageHADomain haDomain = haDomains.get(instanceName); if (haDomain == null) { _logger.error("No StorageHADomain for instanceName: " + instanceName); continue; } updateMetrics(metrics, metricSequence, haDomain, system, keyMap); } } catch (Exception e) { _logger.error("Failed while extracting stats for FEAdapts: ", e); } } private void updateMetrics(String[] metrics, List<String> metricSequence, StorageHADomain haDomain, StorageSystem system, Map<String, Object> keyMap) { // Determine if there were previous metrics values boolean isVmax = StorageSystem.Type.vmax.name().equals(system.getSystemType()); boolean isVnx = StorageSystem.Type.vnxblock.name().equals(system.getSystemType()); Double percentBusy = 0.0; Long idleTicks = 0L; Long cumTicks = 0L; Long ioTicks = 0L; Long iops = 0L; String statisticTime = ""; int count = 0; for (String metric : metricSequence) { FEAdaptMetric mx = FEAdaptMetric.lookup(metric); switch (mx) { case EMCIdleTimeDir: if (isVmax) { idleTicks = ControllerUtils.getModLongValue(metrics[count]); } break; case IdleTimeCounter: if (isVnx) { idleTicks = ControllerUtils.getModLongValue(metrics[count]); } break; case EMCCollectionTimeDir: if (isVmax) { cumTicks = ControllerUtils.getModLongValue(metrics[count]); } break; case IOTimeCounter: if (isVnx) { ioTicks = ControllerUtils.getModLongValue(metrics[count]); } break; case TotalIOs: iops = ControllerUtils.getModLongValue(metrics[count]); break; case StatisticTime: statisticTime = metrics[count]; break; case EMCPercentBusyTime: percentBusy = ControllerUtils.getDoubleValue(metrics[count]); break; default: break; } count++; } // If Vnx, calculate the cumlative ticks as staticTimeMsec * (usecPerMsec / clockTickIntervalUsec) Long statisticTimeMsec = portMetricsProcessor.convertCIMStatisticTime(statisticTime); _logger.info(String.format("%s: IdleTimeDir %d CollectionTimeDir %d IdleTimeCounter %d IOTimeCounter %d" + " Total IOs %d StatisticTime %s StatisticTimeMsec %d", haDomain.getNativeGuid(), idleTicks, cumTicks, idleTicks, ioTicks, iops, statisticTime, statisticTimeMsec)); if (isVnx) { // According to email from Praveen Kumar dated 9/10/2014 2:18 AM CDT: // The clock tick interval is 100 msec. and the value in // CIM_BlockStatisticsCapabilities.CLOCK_TICK_INTERVAL (which is returning 10 usec.) is wrong. Long clockTickIntervalMsec = VNX_CLOCK_TICK_INTERVAL_MSEC; cumTicks = statisticTimeMsec / clockTickIntervalMsec; // Alternate ways of computing the denominator // Long clockTickIntervalUsec = new Long(keyMap.get(Constants.CLOCK_TICK_INTERVAL).toString()); // Long usecPerMsec = portMetricsProcessor.USEC_PER_MSEC; // Use this line to calculate based on the StatisticTime clock interval // cumTicks = statisticTimeMsec * (usecPerMsec/clockTickIntervalUsec); // Use this line to compute based on ratio of idleTicks/(idleTicks+ioTicks) // cumTicks = idleTicks + ioTicks; } if (isVmax && system.checkIfVmax3()) { // VMAX3 systems only return percent busy directly. portMetricsProcessor.processFEAdaptMetrics(percentBusy, iops, haDomain, statisticTime); } else { portMetricsProcessor.processFEAdaptMetrics( idleTicks, cumTicks, iops, haDomain, statisticTime); } } /** * Query the database to get all the HA Domains * * @param dbClient * @param systemURI * @return Map of adapterName to StorageHADomain objects */ public Map<String, StorageHADomain> getHADomainOfSystem(final DbClient dbClient, final URI systemURI) { Map<String, StorageHADomain> haDomains = new HashMap<String, StorageHADomain>(); URIQueryResultList haDomainQueryResult = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint. Factory.getStorageDeviceStorageHADomainConstraint(systemURI), haDomainQueryResult); for (Iterator<URI> haIter = haDomainQueryResult.iterator(); haIter.hasNext();) { StorageHADomain haDomain = dbClient.queryObject(StorageHADomain.class, haIter.next()); // The replace all fixes up SP_A to SP-A so that it will match the metric records. haDomains.put(haDomain.getAdapterName().replaceAll("_", "-"), haDomain); } return haDomains; } public PortMetricsProcessor getPortMetricsProcessor() { return portMetricsProcessor; } public void setPortMetricsProcessor(PortMetricsProcessor portMetricsProcessor) { this.portMetricsProcessor = portMetricsProcessor; } }