diff --git a/checks.d/wmi_check.py b/checks.d/wmi_check.py index c65429defd..44a709dc7f 100644 --- a/checks.d/wmi_check.py +++ b/checks.d/wmi_check.py @@ -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 @@ -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) @@ -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: @@ -47,21 +50,62 @@ 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. @@ -69,12 +113,6 @@ def _extract_metrics(self, results, metrics, tag_by): 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: diff --git a/conf.d/wmi_check.yaml.example b/conf.d/wmi_check.yaml.example index d76b64837a..7d05081c07 100644 --- a/conf.d/wmi_check.yaml.example +++ b/conf.d/wmi_check.yaml.example @@ -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 @@ -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 @@ -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]