You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.7 KiB
182 lines
6.7 KiB
import sys
|
|
import splunk.entity as en
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
sys.path.append(make_splunkhome_path(['etc', 'apps', 'DA-ITSI-CP-vmware-dashboards', 'bin', 'sa_threshold']))
|
|
|
|
from splunklib.searchcommands import \
|
|
dispatch, StreamingCommand, Configuration, Option, validators
|
|
|
|
import operator
|
|
|
|
@Configuration()
|
|
class thresholdlookupCommand(StreamingCommand):
|
|
""" %(synopsis)
|
|
|
|
##Syntax
|
|
|
|
|thresholdlookup entitytype=<entity> perftype=<perftype> metric=<metric> match=True/False
|
|
|
|
##Description
|
|
|
|
This command will look at a "metric" then match it in a sa_threshold.conf file and compare it's value, returning the severity.
|
|
|
|
"""
|
|
entitytype = Option(
|
|
doc='''
|
|
**Syntax:** **entity=***<entity>*
|
|
**Description:** Name of the entitytype to match in sa_threshold.conf''',
|
|
require=True, validate=validators.Fieldname())
|
|
|
|
metric = Option(
|
|
doc='''
|
|
**Syntax:** **metric=***<metric>*
|
|
**Description:** Name of the metric to match in sa_threshold.conf''',
|
|
require=True, validate=validators.Fieldname())
|
|
|
|
match = Option(
|
|
doc='''
|
|
**Syntax:** **match=***<match>*
|
|
**Description:** True/False, used to tell the command if it should compare the metric value or just return the thresholds, True will only return the values, False will compare the values ''',
|
|
require=False, validate=validators.Fieldname())
|
|
|
|
perftype = Option(
|
|
doc='''
|
|
**Syntax:** **perftype=***<perftype>*
|
|
**Description:** Name of the perftype to match in sa_threshold.conf''',
|
|
require=True, validate=validators.Fieldname())
|
|
|
|
def stream(self, events):
|
|
sessionKey=self.service.token
|
|
owner = self.service.namespace["owner"] if self.service.namespace["owner"] else 'nobody'
|
|
app = self.service.namespace["app"]
|
|
self.logger.debug('Building Thresholds...')
|
|
self.logger.debug('CountMatchesCommand: %s' % self) # logs command line
|
|
search = "entitytype={0} perftype={1} metric={2} disabled=0".format(self.entitytype, self.perftype, self.metric)
|
|
targetEntity = list(get_threshold_stanzas(search=search, app=app, owner=owner, sessionKey=sessionKey).values())
|
|
if len(targetEntity) > 0:
|
|
targetEntity = targetEntity[0]
|
|
targetEntity_properties = get_threshold_stanza(
|
|
name=targetEntity.name,
|
|
app=app, owner=owner, sessionKey=sessionKey)
|
|
else:
|
|
targetEntity = False
|
|
matchFields = True
|
|
if self.match:
|
|
matchValue = self.match.lower()
|
|
if matchValue == "true" or matchValue =="t" or matchValue == "1":
|
|
matchFields = True
|
|
else:
|
|
matchFields = False
|
|
|
|
self.logger.debug("Target Stanza: '%s'", targetEntity)
|
|
|
|
# avoid repeated indexing operations
|
|
metric = self.metric
|
|
if targetEntity:
|
|
threshold = targetEntity_properties
|
|
else:
|
|
threshold = {}
|
|
thresholdCrit = threshold.get('critical', None)
|
|
thresholdWarn = threshold.get('warning', None)
|
|
if thresholdCrit is None:
|
|
thresholdCrit = ""
|
|
else:
|
|
thresholdCrit = float(thresholdCrit)
|
|
if thresholdWarn is None:
|
|
thresholdWarn = ""
|
|
else:
|
|
thresholdWarn = float(thresholdWarn)
|
|
# Setting severity for matchFields=true
|
|
if matchFields:
|
|
comparator = threshold.get('comparator','')
|
|
else:
|
|
comparators = {'<': operator.lt,
|
|
'<=': operator.le,
|
|
'==': operator.eq,
|
|
'>=': operator.ge,
|
|
'>': operator.gt}
|
|
comparator = comparators[threshold.get('comparator', '==')]
|
|
# Setting result attributes
|
|
for event in events:
|
|
self.logger.debug("Contains target metric : '%s'", (metric in event) )
|
|
self.logger.debug("Results '%s'", event )
|
|
# Check for macthFields again and avoid co,
|
|
if matchFields or (threshold.get('comparator', None) is None):
|
|
event['threshold_severity'] = "unchecked"
|
|
else:
|
|
if not all(v == "" for v in event.values()):
|
|
if metric in event and event[metric] != '':
|
|
try:
|
|
curr = float(event[metric])
|
|
except ValueError:
|
|
self.logger.error( "Metric value could not be converted to float: %s | event: %s" % (event[metric],event))
|
|
event['threshold_severity'] = "unknown"
|
|
continue
|
|
self.logger.debug("Results Metric Value: '%s'", event[metric])
|
|
if comparator(curr, thresholdCrit):
|
|
self.logger.debug( "Value is critical")
|
|
event['threshold_severity'] = "critical"
|
|
elif comparator(curr, thresholdWarn):
|
|
self.logger.debug( "Value is warning")
|
|
event['threshold_severity'] = "warning"
|
|
else :
|
|
self.logger.debug( "Value is normal")
|
|
event['threshold_severity'] = "normal"
|
|
else :
|
|
self.logger.debug( "Metric Missing" )
|
|
event['threshold_severity'] = "unknown"
|
|
event['threshold_critlevel'] = thresholdCrit
|
|
event['threshold_warnlevel'] = thresholdWarn
|
|
event['threshold_comparator'] = threshold.get('comparator', '')
|
|
self.logger.debug("Yielding this event to splunk: %s", event )
|
|
yield event
|
|
|
|
# Returns all threshold stanzas.
|
|
def get_threshold_stanzas(app, owner, sessionKey, search=None):
|
|
# NOTE: Can't use the built-in REST API search parameter because
|
|
# it doesn't work with this endpoint.
|
|
results = en.getEntities(
|
|
# NOTE: Can't use 'configs/conf-sa_threshold' because it is only accessible
|
|
# to the 'admin' user (or a user with the 'admin_all_objects' capability).
|
|
entityPath='properties/sa_threshold',
|
|
namespace=app, owner=owner,
|
|
sessionKey=sessionKey
|
|
)
|
|
|
|
if search is not None:
|
|
# Convert search string from traditional format to dictionary
|
|
search_kvs = [kv.split('=') for kv in search.split(' ')]
|
|
|
|
def matches_search(entity):
|
|
props = get_threshold_stanza(entity.name, app, owner, sessionKey)
|
|
|
|
for (k, v) in search_kvs:
|
|
# NOTE: Special-case the 'disabled' property with its default
|
|
# value so that the old search string still works.
|
|
prop_value = props.get(k, None)
|
|
if prop_value is None and k == 'disabled':
|
|
prop_value = '0'
|
|
|
|
if prop_value is None or str(prop_value) != v:
|
|
return False
|
|
return True
|
|
|
|
# NOTE: Fetches every stanza individually as a side effect.
|
|
# No choice, since the REST API doesn't help us.
|
|
# Hopefully the impact is minimal since this code is running server-side.
|
|
results = {k : v for (k, v) in results.items() if matches_search(v)}
|
|
|
|
return results
|
|
|
|
# Given a stanza name, returns a dictionary of key-value pairs.
|
|
def get_threshold_stanza(name, app, owner, sessionKey):
|
|
stanza_kvpairs = en.getEntities(
|
|
entityPath='properties/sa_threshold/' + name,
|
|
namespace=app, owner=owner,
|
|
sessionKey=sessionKey
|
|
).values()
|
|
|
|
return {kv.name : kv.value for kv in stanza_kvpairs}
|
|
|
|
dispatch(thresholdlookupCommand, sys.argv, sys.stdin, sys.stdout, __name__)
|