Skip to content

Commit

Permalink
Merge pull request DataDog#1388 from DataDog/yann/wmi-extend-tagging
Browse files Browse the repository at this point in the history
[wmi] extend tagging for related class & constants
  • Loading branch information
yannmh committed Mar 14, 2015
2 parents 31c08ec + d1af6ea commit bb723d0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 22 deletions.
68 changes: 53 additions & 15 deletions checks.d/wmi_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Windows Only.
Generic WMI check. This check allows you to specify particular metrics that you
want from WMI in your configuration. Check wmi.yaml.example in your conf.d
want from WMI in your configuration. Check wmi_check.yaml.example in your conf.d
directory for more details on configuration.
'''
# project
Expand All @@ -14,6 +14,7 @@
UP_METRIC = 'Up'
SEARCH_WILDCARD = '*'


class WMICheck(AgentCheck):
def __init__(self, name, init_config, agentConfig):
AgentCheck.__init__(self, name, init_config, agentConfig)
Expand All @@ -35,9 +36,11 @@ def check(self, instance):
metrics = instance.get('metrics')
filters = instance.get('filters')
tag_by = instance.get('tag_by')
tag_queries = instance.get('tag_queries')
constant_tags = instance.get('constant_tags')

if not wmi_class:
raise Exception('WMI instance is missing a value for `class` in wmi.yaml')
raise Exception('WMI instance is missing a value for `class` in wmi_check.yaml')

# If there are filters, we need one query per filter.
if filters:
Expand All @@ -47,34 +50,69 @@ def check(self, instance):
if SEARCH_WILDCARD in search:
search = search.replace(SEARCH_WILDCARD, '%')
wql = "SELECT * FROM %s WHERE %s LIKE '%s'" \
% (wmi_class, prop, search)
% (wmi_class, prop, search)
results = w.query(wql)
else:
results = getattr(w, wmi_class)(**f)
self._extract_metrics(results, metrics, tag_by)
self._extract_metrics(results, metrics, tag_by, w, tag_queries, constant_tags)
else:
results = getattr(w, wmi_class)()
self._extract_metrics(results, metrics, tag_by)
self._extract_metrics(results, metrics, tag_by, w, tag_queries, constant_tags)

def _extract_metrics(self, results, metrics, tag_by):
def _extract_metrics(self, results, metrics, tag_by, wmi, tag_queries, constant_tags):
if len(results) > 1 and tag_by is None:
raise Exception('WMI query returned multiple rows but no `tag_by` value was given. metrics=%s' % metrics)
raise Exception('WMI query returned multiple rows but no `tag_by` value was given. '
'metrics=%s' % metrics)

for res in results:
tags = []

# include any constant tags...
if constant_tags:
tags.extend(constant_tags)

# if tag_queries is specified then get attributes from other classes and use as a tags
if tag_queries:
for query in tag_queries:
link_source_property = int(getattr(res, query[0]))
target_class = query[1]
link_target_class_property = query[2]
target_property = query[3]

link_results = \
wmi.query("SELECT {0} FROM {1} WHERE {2} = {3}"
.format(target_property, target_class,
link_target_class_property, link_source_property))

if len(link_results) != 1:
self.log.warning("Failed to find {0} for {1} {2}. No metrics gathered"
.format(target_class, link_target_class_property,
link_source_property))
continue

for wmi_property, name, mtype in metrics:
for res in results:
link_value = str(getattr(link_results[0], target_property)).lower()
tags.append("{0}:{1}".format(target_property.lower(),
"_".join(link_value.split())))

# Grab the tag from the result if there's a `tag_by` value (e.g.: "name:jenkins")
# Strip any #instance off the value when `tag_queries` is set (gives us unique tags)
if tag_by:
tag_value = getattr(res, tag_by).lower()
if tag_queries and tag_value.find("#") > 0:
tag_value = tag_value[:tag_value.find("#")]
tags.append('%s:%s' % (tag_by.lower(), tag_value))

if len(tags) == 0:
tags = None

for wmi_property, name, mtype in metrics:
if wmi_property == UP_METRIC:
# Special-case metric will just submit 1 for every value
# returned in the result.
val = 1
else:
val = float(getattr(res, wmi_property))

# Grab the tag from the result if there's a `tag_by` value (e.g.: "name:jenkins")
if tag_by:
tags = ['%s:%s' % (tag_by.lower(), getattr(res, tag_by))]
else:
tags = None

try:
func = getattr(self, mtype)
except AttributeError:
Expand Down
41 changes: 34 additions & 7 deletions conf.d/wmi_check.yaml.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
init_config:

instances:
# Each WMI query has 2 required options, `class` and `metrics` and two
# optional options, `filters` and `tag_by`.
# Each WMI query has 2 required options, `class` and `metrics` and four
# optional options, `filters`, `tag_by`, `constant_tags` and `tag_queries`.
#
# `class` is the name of the WMI class, for example Win32_OperatingSystem
# or Win32_PerfFormattedData_PerfProc_Process. You can find many of the
Expand Down Expand Up @@ -35,21 +35,44 @@ instances:
# WMI class you're using. This is only useful when you will have multiple
# values for your WMI query. The examples below show how you can tag your
# process metrics with the process name (giving a tag of "name:app_name").
#
# `constant_tags` optionally lets you tag each metric with a set of fixed values
#
# `tag_queries` optionally lets you specify a list of queries, to tag metrics
# with a target class property. Each item in the list is a set of
# [link source property, target class, link target class property, target property]
# where:
#
# - 'link source property' contains the link value
#
# - 'target class' is the class to link to
#
# - 'link target class property' is the target class property to link to
#
# - 'target property' contains the value to tag with
#
# It translates to a WMI query:
# SELECT 'target property' FROM 'target class'
# WHERE 'link target class property' = 'link source property'
#
# Note: setting this will cause any instance number to be removed from tag_by values
# i.e. name:process#1 => name:process


# Fetch the number of processes and users
# Fetch the number of processes and users.
- class: Win32_OperatingSystem
metrics:
- [NumberOfProcesses, system.proc.count, gauge]
- [NumberOfUsers, system.users.count, gauge]

# Fetch metrics for a single running application, called myapp
# Fetch metrics for a single running application, called myapp, tagging with 'role:test'.
- class: Win32_PerfFormattedData_PerfProc_Process
metrics:
- [ThreadCount, my_app.threads.count, gauge]
- [VirtualBytes, my_app.mem.virtual, gauge]
filters:
- Name: myapp
constant_tags:
- 'role:test'

# Fetch process metrics for a set of processes, tagging by app name.
- class: Win32_PerfFormattedData_PerfProc_Process
Expand All @@ -63,9 +86,13 @@ instances:
- Name: app3
tag_by: Name

# Fetch process metrics for every available process, tagging by app name.
# Fetch process metrics for a set of processes, tagging by app name, and command line params.
- class: Win32_PerfFormattedData_PerfProc_Process
metrics:
- [IOReadBytesPerSec, proc.io.bytes_read, gauge]
- [IOWriteBytesPerSec, proc.io.bytes_written, gauge]
filters:
- Name: app1
- Name: app2
tag_by: Name
tag_queries:
- [IDProcess, Win32_Process, Handle, CommandLine]

0 comments on commit bb723d0

Please sign in to comment.