/*
* Copyright (c) 2008-2011 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.plugins.metering.smis.processor;
import com.emc.storageos.cimadapter.connections.cim.CimObjectPathCreator;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.Stat;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.plugins.common.Constants;
import com.emc.storageos.plugins.common.domainmodel.Operation;
import com.emc.storageos.plugins.metering.smis.SMIPluginException;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.plugins.metering.CassandraInsertion;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.cim.CIMArgument;
import javax.cim.CIMDataType;
import javax.cim.CIMObjectPath;
import javax.cim.CIMProperty;
import java.util.List;
import java.util.Map;
/**
* VolumeProcessor used in retrieving ReadIOs,WriteIOs,nativeGuid
*/
public class VolumeProcessor extends CommonStatsProcessor {
/*
* Getting CIMObjectPaths for Volumes, is a single SMI-S Call, and if 1000s
* of volumes are there, the time taken to retrieve them is huge, and its
* going to impact performance a lot too.Hence, manually at runtime using
* the volume IDs got from Statistics, I have created Volume CIMObjectPaths,
* rather than getting it from Provider. Its a huge performance gain, but
* the down side is having to identify the type, i.e. Symmetrix or Clariion
* at runtime.
*
* To-Do: Currently,Volume Instances, paths got from Providers are kept in
* Memory, and are short lived till the job dies.But if really it becomes a
* scale problem,need to decide on using Native Heaps solutions like
* EhCache, Memcache.
*/
private Logger _logger = LoggerFactory.getLogger(VolumeProcessor.class);
private CassandraInsertion _statsColumnInjector;
public static enum VolumeMetric
{
UnKnown,
InstanceID,
ElementType,
KBytesWritten,
KBytesRead,
TotalIOs,
ReadIOs,
WriteIOs,
KBytesTransferred,
IdleTimeCounter,
IOTimeCounter,
EMCQueueLength;
private static final VolumeMetric[] metricCopyOfValues = values();
public static VolumeMetric lookup(String name) {
for (VolumeMetric value : metricCopyOfValues) {
if (value.name().equals(name)) {
return value;
}
}
return UnKnown;
}
}
@Override
public void processResult(
Operation operation, Object resultObj, Map<String, Object> keyMap)
throws SMIPluginException {
long timeinMilis;
try {
timeinMilis = (Long) keyMap.get(Constants._TimeCollected);
CIMArgument<?>[] outputArguments = (CIMArgument<?>[]) resultObj;
@SuppressWarnings("unchecked")
List<Stat> metricsObjList = (List<Stat>) keyMap.get(Constants._Stats);
DbClient dbClient = (DbClient) keyMap.get(Constants.dbClient);
if ((null == outputArguments[0]) ||
((String[]) outputArguments[0].getValue()).length == 0) {
_logger.warn("Empty Statistics response returned from Provider");
return;
}
String[] volumes = ((String[]) outputArguments[0].getValue())[0].split("\n");
List<String> metricSequence = (List<String>) keyMap.get(Constants.STORAGEOS_VOLUME_MANIFEST);
_logger.debug("volume metricNames Sequence {}", metricSequence);
for (String volume : volumes) {
if (volume.isEmpty()) {
_logger.debug("Empty Volume returned as part of Statistics Response");
continue;
}
if (null != metricSequence && !metricSequence.isEmpty()) {
addMetricstoMetricsObject(keyMap, volume, metricsObjList, timeinMilis, metricSequence, dbClient);
} else {
_logger.error("failed processing Volume Metric values as metric sequence is null.");
}
}
_zeroRecordGenerator.identifyRecordstobeZeroed(keyMap, metricsObjList, Volume.class);
} catch (Exception e) {
_logger.error(
"Failed while extracting Read & WriteIOs for Volumes : ", e);
}
resultObj = null;
}
/**
* Create CIMObjectPath using the nativeGUID got from Statistics Call.
*
* @param nativeGUID
* @param symmsystem
* @param symmvolume
* @return CIMObjectPath
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private CIMObjectPath createCIMPath(
String nativeGUID, String volume, String system, Map<String, Object> keyMap) {
String[] tokens = nativeGUID.split(Constants.PATH_DELIMITER_REGEX);
String SystemName = tokens[0] + Constants._plusDelimiter + tokens[1];
String deviceID = tokens[3];
// CLARIION+APM00120400480+VOLUME+UID+600601605B1029004B318218D588E111 -
// Snapshot targets format
if (tokens.length > 4) {
for (int i = 4; i < tokens.length; i++) {
deviceID = deviceID + "+" + tokens[i];
}
}
CIMProperty<?> CreationClassName = new CIMProperty(CreationClassNamestr,
CIMDataType.STRING_T, volume, true, false, null);
CIMProperty<?> SystemCreationClassName = new CIMProperty(
SystemCreationClassNamestr, CIMDataType.STRING_T, system, true, false, null);
CIMProperty<?> systemName = new CIMProperty(SystemNamestr, CIMDataType.STRING_T,
SystemName, true, false, null);
CIMProperty<?> Id = new CIMProperty(DeviceIDstr, CIMDataType.STRING_T, deviceID, true, false, null);
CIMProperty<?>[] keys = new CIMProperty<?>[4];
keys[0] = CreationClassName;
keys[1] = SystemCreationClassName;
keys[2] = systemName;
keys[3] = Id;
// To-DO : "root/emc - get it from outside"
return CimObjectPathCreator.createInstance(volume, keyMap.get(Constants._InteropNamespace)
.toString(), keys);
}
/**
* Parse NativeGuid, create Volume CIMObjectPaths by using nativeGuids. Get
* the associated Metrics Object for each Volume, update with
* ReadIOs,WriteIOs, guid content.
*
* string CSVSequence[] = InstanceID,
* ElementType, TotalIOs, KBytesTransferred, ReadIOs, KBytesRead, WriteIOs,
* KBytesWritten;
*
* @param keyMap
* @param volume
* @param metricsObjList
* @throws SMIPluginException
*/
private void addMetricstoMetricsObject(
Map<String, Object> keyMap, String volume, List<Stat> metricsObjList,
long timeinMillis, List<String> metricSequence, DbClient dbClient) throws SMIPluginException {
String nativeGuid = null;
try {
_logger.debug("Volumes :" + volume);
Iterable<String> splitIterator = Splitter.on(Constants.SEMI_COLON).split(volume);
List<String> metricValuesList = Lists.newLinkedList(splitIterator);
// translated Attributes is needed to create CIMObject at runtime
// without querying it from Provider,
// which increases performance a lot, by reducing SMI-S calls
nativeGuid = translatedAttributes(metricValuesList.get(0).toUpperCase(), keyMap);
CIMObjectPath path = null;
if (nativeGuid.contains(_symm)) {
path = createCIMPath(nativeGuid, _symmvolume, _symmsystem, keyMap);
} else if (nativeGuid.contains(_clar)) {
path = createCIMPath(nativeGuid, _clarvolume, _clarsystem, keyMap);
}
// Inject Project, COS, tenant ID
Stat statObj = _zeroRecordGenerator.injectattr(keyMap, nativeGuid, null);
if (statObj != null) {
@SuppressWarnings("unchecked")
List<CIMObjectPath> volList = (List<CIMObjectPath>) keyMap.get(Constants._Volumes);
volList.add(path);
statObj.setServiceType(Constants._Block);
statObj.setTimeCollected((Long) keyMap.get(Constants._TimeCollected));
statObj.setTimeInMillis(timeinMillis);
_statsColumnInjector.injectColumns(statObj, dbClient);
// Default Capacity in Model is -1. As snapshotCount and
// capacity is cumulative Count of multiple Snapshots, making to
// default 0
// for each retrieved Volume.
int count = 0;
for (String metricName : metricSequence) {
String metricValue = metricValuesList.get(count);
switch (VolumeMetric.lookup(metricName)) {
case InstanceID:
case ElementType:
count++;
break;
case KBytesWritten:
statObj.setBandwidthIn(ControllerUtils.getLongValue(metricValue));
count++;
break;
case KBytesRead:
statObj.setBandwidthOut(ControllerUtils.getLongValue(metricValue));
count++;
break;
case TotalIOs:
statObj.setTotalIOs(ControllerUtils.getLongValue(metricValue));
count++;
break;
case ReadIOs:
statObj.setReadIOs(ControllerUtils.getLongValue(metricValue));
count++;
break;
case WriteIOs:
statObj.setWriteIOs(ControllerUtils.getLongValue(metricValue));
count++;
break;
case KBytesTransferred:
statObj.setKbytesTransferred(ControllerUtils.getLongValue(metricValue));
count++;
break;
case IdleTimeCounter:
if (null != metricValue && 0 < metricValue.trim().length()) {
statObj.setIdleTimeCounter(ControllerUtils.getLongValue(metricValue));
}
count++;
break;
case IOTimeCounter:
if (null != metricValue && 0 < metricValue.trim().length()) {
statObj.setIoTimeCounter(ControllerUtils.getLongValue(metricValue));
}
count++;
break;
case EMCQueueLength:
if (null != metricValue && 0 < metricValue.trim().length()) {
statObj.setQueueLength(ControllerUtils.getLongValue(metricValue));
}
count++;
break;
default:
_logger.warn("Ignoring unknown metric {} during system metric processing:", metricName);
count++;
break;
}
}
metricsObjList.add(statObj);
}
} catch (Exception ex) {
_logger.error("Processing Volume : {} failed : ", volume, ex);
}
}
public void setStatsColumnInjector(CassandraInsertion statsColumnInjector) {
_statsColumnInjector = statsColumnInjector;
}
public CassandraInsertion getStatsColumnInjector() {
return _statsColumnInjector;
}
}