/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.cimadapter.processors;
// Java imports
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TimeZone;
import javax.cim.CIMDataType;
import javax.cim.CIMDateTimeAbsolute;
import javax.cim.CIMInstance;
import javax.cim.CIMObjectPath;
import javax.cim.CIMProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.cimadapter.connections.cim.CimAlertType;
import com.emc.storageos.cimadapter.connections.cim.CimCompositeID;
import com.emc.storageos.cimadapter.connections.cim.CimConstants;
import com.emc.storageos.cimadapter.connections.cim.CimObjectPathCreator;
/**
* Encapsulates a CIM indication as a table of name/value pairs.
*/
public class CimIndicationSet {
// The CIM indication as a set of name/value pairs.
private Hashtable<String, String> _pairs = new Hashtable<String, String>();
// A logger reference.
private static final Logger s_logger = LoggerFactory
.getLogger(CimIndicationSet.class);
/**
* Constructs a CimIndicationSet from the given indication.
*/
public CimIndicationSet(CIMInstance indication) {
processInstance(indication);
setIndicationClass();
setImpactedClassTags();
if (isAlertIndication()) {
setAlertTags();
}
}
/**
* Determines if there is a name/value pair with the passed name.
*
* @param key The key representing the name.
*
* @return true if there is an entry with the passed key.
*/
public boolean containsKey(Object key) {
return _pairs.containsKey(key);
}
/**
* Returns the table of name/value pairs.
*
* @return The table of name/value pairs.
*/
public Hashtable<String, String> getMap() {
return _pairs;
}
/**
* Gets the value associated with the passed name.
*
* @param name The key for the desired entry.
*
* @return The value for the passed name.
*/
public String get(String name) {
return _pairs.get(name);
}
/**
* Add or replace a name/value pair. If 'value' is null, the name is
* removed.
*
* @param name The name to be added to the table.
* @param value The associated value to be added top the table.
*/
public void set(String name, String value) {
internalSet(name, value);
}
/**
* Add or replace a name/value pair. If 'value' is null, the name is
* removed.
*
* @param name The name to be added to the table.
* @param value The associated value to be added top the table.
*/
private void internalSet(String name, String value) {
if (value != null) {
_pairs.put(name, value);
} else {
_pairs.remove(name);
}
}
/**
* Removes the name/value pair with the passed name.
*
* @param key The key represented the entry to be removed.
*
* @return The value for the entry being removed, or null if there is no
* entry.
*/
public String remove(Object key) {
return _pairs.remove(key);
}
/**
* Returns the names for the entries in the table.
*
* @return The list of names for which there are entries in the table.
*/
public synchronized Enumeration<String> getNames() {
return _pairs.keys();
}
/**
* Determines whether or not the indication is an alert.
*
* @return true if the indication is a CIM_AlertIndication, otherwise false.
*/
public final boolean isAlertIndication() {
return containsKey(CimConstants.ALERT_INDICATION_KEY);
}
/**
* Determines whether or not the indication is an instance indication.
*
* @return true if the indication is a CIM_InstIndication, otherwise false.
*/
public final boolean isInstIndication() {
return containsKey(CimConstants.INST_INDICATION_KEY);
}
/**
* Adds name-value pairs for the given CIM object instance. A pair is
* created for the CIM class. Then each property is processed to create more
* name-value pairs.
*
*
* Example:
*
* (logged instance) SourceInstance = instance of Clar_FrontEndFCPort {
* CreationClassName = "Clar_FrontEndFCPort"; DeviceID =
* "CLARiiON+FNM00083700232+SP_B+0"; }; ;
*
* (name-value pairs with prefix "SourceInstance")
* SourceInstanceClassName|Clar_FrontEndFCPort
* SourceInstanceCreationClassName|Clar_FrontEndFCPort
* SourceInstanceDeviceID|CLARiiON+FNM00083700232+SP_B+0
*
* @param prefix the prefix that is applied to value names
* @param instance the CIM object instance
*/
public void processInstance(String prefix, CIMInstance instance) {
set(prefix + CimConstants.CLASS_NAME_KEY, instance.getClassName());
for (CIMProperty<?> p : instance.getProperties()) {
processProperty(prefix, p);
}
}
/**
* Adds name-value pairs for the given CIM object instance.
*
* @param instance the CIM object instance
*/
public void processInstance(CIMInstance instance) {
processInstance("", instance);
}
/**
* Adds name-value pairs for the given CIM object path. A pair is created
* for each key, and a {@link CimCompositeID} is formulated and stored as an
* additional name-value pair.
*
* Example:
*
* (logged object path) AlertingManagedElement =
* "//172.23.120.60/root/emc:clar_diskdrive
* .CreationClassName=\"Clar_DiskDrive\",DeviceID=\"CLARiiON+2_0_2\"
* ,SystemCreationClassName=\"Clar_StorageSystem\",SystemName=\"CLAR
* iiON+FNM00083700232\"";
*
* (name-value pairs with prefix "AlertingManagedElement")
* AlertingManagedElementClassName|clar_diskdrive
* AlertingManagedElementCreationClassName|Clar_DiskDrive
* AlertingManagedElementDeviceID|CLARiiON+2_0_2
* AlertingManagedElementSystemCreationClassName|Clar_StorageSystem
* AlertingManagedElementSystemName|CLARiiON+FNM00083700232
* AlertingManagedElementCompositeID|CLARiiON+FNM00083700232/CLARiiO N+2_0_2
*
* @param prefix the prefix that is applied to value names
* @param path the CIM object path
*/
public void processObjectPath(String prefix, CIMObjectPath path) {
if (path.toString().length() == 0) {
return;
}
set(prefix + CimConstants.CLASS_NAME_KEY, path.getObjectName());
set(prefix + CimConstants.COMPOSITE_ID_KEY, CimCompositeID.parsePath(path));
for (CIMProperty<?> p : path.getKeys()) {
processProperty(prefix, p);
}
}
/**
* Adds name-value pairs for the given CIM object path.
*
* @param path the CIM object path
*/
public void processObjectPath(CIMObjectPath path) {
processObjectPath("", path);
}
/**
* Adds name-value pairs for the given CIM property. Many property types
* result in a single name-value pair, but some explode into multiple
* name-value pairs. The given prefix is applied to each name.
*
*
* <b>CIM Object Path Arrays:</b>
*
* A set of indexed name-value pairs is made for each element. An additional
* name-value pair is added to store the number of non -null elements. Null
* elements are not included.
*
* Example:
*
* (what this probably looks like when logged) DeadPresidents = {
* "//172.23.120.128/root/ustreasury/President.Creation
* ClassName=\"MonetizedPresident\",LastName=\"Washington\"
* ,FirstName=\"George\"",
* "//172.23.120.128/root/ustreasury/President.Creation
* ClassName=\"MonetizedPresident\",LastName=\"Lincoln\",Fi
* rstName=\"Abraham\"" };
*
* (name-value pairs with prefix "cashDeadPresidents")
* cashDeadPresidentsCount|2 cashDeadPresidents0ClassName|President
* cashDeadPresidents0CreationClassName|MonetizedPresident
* cashDeadPresidents0LastName|Washington
* cashDeadPresidents0FirstName|George
* cashDeadPresidents1ClassName|President
* cashDeadPresidents1CreationClassName|MonetizedPresident
* cashDeadPresidents1LastName|Lincoln cashDeadPresidents1FirstName|Abraham
*
*
* <b>CIM Instance Arrays</b>
*
* A set of indexed name-value pairs is made for each element. An additional
* name-value pair is added to store the number of non -null elements. Null
* elements are not included.
*
* Example:
*
* (what this probably looks like when it is logged) NeedfulThings = {
* instance of EvilItem { CreationClassName = "ForbiddenFruit"; Location =
* "Garden of Eden"; }, instance of EvilItem { CreationClassName =
* "MagicRing"; Owner = "Sauron"; } };
*
* (name-value pairs with prefix "mammonNeedfulThings")
* mammonNeedfulThingsCount|2 mammonNeedfulThings0ClassName|EvilItem
* mammonNeedfulThings0CreationClassName|ForbiddenFruit
* mammonNeedfulThings0Location|Garden of Eden
* mammonNeedfulThings1ClassName|EvilItem
* mammonNeedfulThings1CreationClassName|MagicRing
* mammonNeedfulThings1Owner|Sauron
*
*
* <b>Other (Scalar) Arrays:</b>
*
* Adds a name-value pair that contains a comma-separated list of the
* elements. It is assumed that these values won't contain a comma
* themselves in use cases where it matters, so an escape mechanism is
* currently not implemented. Empty arrays are not processed.
*
* Example:
*
* (logged array) SourceInstance = instance of Clar_FrontEndFCPort {
* OperationalStatus = { 10,2 }; };
*
* (name-value pair with prefix "SourceInstance")
* SourceInstanceOperationalStatus|10,2
*
*
* <b>CIM Object Paths:</b>
*
* See @processObjectPath
*
*
* <b>CIM Object Instances:</b>
*
* See @processInstance
*
*
* <b>Strings That Represent CIM Object Paths</b>
*
* If a string "looks" like an object path, an attempt is made to process it
* as an object path in addition to processing it as an "ordinary" scalar.
*
*
* <b>(Ordinary) Scalars:</b>
*
* Example:
*
* (logged scalar) PerceivedSeverity = 2;
*
* (name-value pairs with prefix "") PerceivedSeverity|2
*
*
* @param prefix the prefix that is applied to value names
* @param p the CIM property
*/
public void processProperty(String prefix, CIMProperty<?> p) {
String name = prefix + p.getName();
int type = p.getDataType().getType();
if (p.getValue() == null) {
// A property value can be a null reference!
// This won't show up when the indication is
// logged.
return;
}
if (p.getDataType().isArray()) {
switch (type) {
case CIMDataType.REFERENCE:
// CIM object path array
for (Object o : (Object[]) p.getValue()) {
if (o instanceof CIMObjectPath) {
processObjectPath(name, (CIMObjectPath) o);
}
}
break;
case CIMDataType.OBJECT:
// CIM object instance array
int count = 0;
for (Object o : (Object[]) p.getValue()) {
if (o instanceof CIMInstance) {
processInstance(name + count, (CIMInstance) o);
count++;
}
}
if (count > 0) {
set(name + CimConstants.COUNT_KEY, Integer.toString(count));
}
break;
default:
// Array of scalars
StringBuilder buffer = new StringBuilder();
for (Object o : (Object[]) p.getValue()) {
if (o == null) {
continue;
}
buffer.append(',');
buffer.append(o.toString());
}
if (buffer.length() > 1) {
// Remove the leading comma
set(name, buffer.substring(1));
} else {
// Empty array
return;
}
}
} else {
switch (type) {
case CIMDataType.DATETIME:
// Date/time is converted to long. Milliseconds are truncated.
// s_logger.debug("Date time property is {}", name);
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
String dateTimeStr = p.getValue().toString();
// s_logger.debug("Date time value is {}", dateTimeStr);
try {
CIMDateTimeAbsolute cd = new CIMDateTimeAbsolute(dateTimeStr);
c.set(cd.getYear(), cd.getMonth() - 1, cd.getDay(), cd.getHour(),
cd.getMinute(), cd.getSecond());
c.set(Calendar.MILLISECOND, 0);
} catch (Exception e) {
// It seems that date time properties may not be set and the
// value causes an exception when you create a
// CIMDateTimeAbsolute. Just set the date to the start of
// the epoch for this error condition.
s_logger.debug("Date and time are not set for property {}.", name);
c.setTimeInMillis(0);
}
// s_logger.debug("Date for property {} is {}", name, c.getTime().toString());
set(name, String.valueOf(c.getTimeInMillis()));
break;
case CIMDataType.REFERENCE:
// CIM object path
processObjectPath(name, (CIMObjectPath) p.getValue());
break;
case CIMDataType.OBJECT:
// CIM object instance
processInstance(name, (CIMInstance) p.getValue());
break;
case CIMDataType.STRING:
// String. Check to see if it represents
// an object path. If so, try to process
// it as an object path. No matter what,
// fall to the default, so that it can
// be processed as an ordinary scalar.
String value = p.getValue().toString();
// Start with an inexpensive test.
// Does it look anything like a WBEM URI?
if (value.matches("/+[\\w\\.\\d/]+.*")) {
// Confirm with an expensive test.
// Can we make a CIMObjectPath from it?
CIMObjectPath path = null;
try {
path = CimObjectPathCreator.createInstance(value);
} catch (Exception e) {
s_logger.error(e.getMessage(), e);
}
if (path != null) {
processObjectPath(name, path);
}
}
// Continue processing this as an ordinary scalar
default:
// Should be an ordinary scalar
set(name, p.getValue().toString());
}
}
}
/**
* Adds name-value pairs for the given CIM property.
*
* @param p the CIM property
*/
public void processProperty(CIMProperty<?> p) {
processProperty("", p);
}
/**
* Sets name-value pairs:
*
* IndicationClassName IndicationClassTag
*/
private void setIndicationClass() {
// Replace top-level "ClassName" with "IndicationClassName"
String key = CimConstants.CLASS_NAME_KEY;
String name = get(key);
set(CimConstants.INDICATION_CLASS_NAME_KEY, name);
remove(key);
// Add a name-value pair for the rule set to reference
// as the "Generic" field in the input that is going
// to be sent to NOTIF. The indication class name is
// appropriate, but underscores must be replaced with
// periods to match the corresponding ECI names that
// we like to use -- Periods help NOTIF organize the
// ECI view.
String tag = name.replace('_', '.');
set(CimConstants.INDICATION_CLASS_TAG_KEY, tag);
}
/**
* Sets name-value pairs:
*
* AlertingManagedElementClassPrefixTag AlertingManagedElementClassSuffixTag
* SourceInstanceModelPathClassPrefixTag
* SourceInstanceModelPathClassSuffixTag
*/
private void setImpactedClassTags() {
String prefix = null;
if (isAlertIndication()) {
prefix = CimConstants.ALERT_MANAGED_ELEM_CLASS_KEY;
} else if (isInstIndication()) {
prefix = CimConstants.SRC_INST_MODEL_PATH_CLASS_KEY;
}
if (prefix == null) {
return;
}
String name = get(prefix + CimConstants.NAME_KEY);
if (name == null) {
return;
}
String[] tokens = name.split("_", 2);
set(prefix + CimConstants.PREFIX_TAG_KEY, tokens[0]);
if (tokens.length == 2) {
set(prefix + CimConstants.SUFFIX_TAG_KEY, tokens[1]);
}
}
/**
* Sets name-value pairs:
*
* AlertTypeTag ProbableCauseTag
*/
private void setAlertTags() {
String key = CimConstants.ALERT_TYPE_KEY;
if (containsKey(key)) {
String tag = "";
try {
int i = Integer.parseInt(get(key));
tag = CimAlertType.toString(i);
} catch (NumberFormatException e) {
s_logger.error(e.getMessage(), e);
}
set(CimConstants.ALERT_TYPE_TAG_KEY, tag);
key = CimConstants.PROBABLE_CAUSE_DESCR_KEY;
if (containsKey(key)) {
// tag = NvTag.toCamelCase(get(key), true);
set(CimConstants.PROBABLE_CAUSE_TAG_KEY, tag);
}
}
}
}