appoptics_return.py (salt-3002.1) | : | appoptics_return.py (salt-3002.2) | ||
---|---|---|---|---|
# -*- coding: utf-8 -*- | ||||
"""Salt returner to return highstate stats to AppOptics Metrics | """Salt returner to return highstate stats to AppOptics Metrics | |||
To enable this returner the minion will need the AppOptics Metrics | To enable this returner the minion will need the AppOptics Metrics | |||
client importable on the Python path and the following | client importable on the Python path and the following | |||
values configured in the minion or master config. | values configured in the minion or master config. | |||
The AppOptics python client can be found at: | The AppOptics python client can be found at: | |||
https://github.com/appoptics/python-appoptics-metrics | https://github.com/appoptics/python-appoptics-metrics | |||
skipping to change at line 69 | skipping to change at line 68 | |||
This will report success and failure counts on runs of the | This will report success and failure counts on runs of the | |||
``role_salt_master.netapi``, ``role_redis.config``, and | ``role_salt_master.netapi``, ``role_redis.config``, and | |||
``role_smarty.dummy`` states in addition to highstates. | ``role_smarty.dummy`` states in addition to highstates. | |||
This will report the same metrics as above, but for these runs the | This will report the same metrics as above, but for these runs the | |||
metrics will be tagged with ``state_type: sls`` and ``state_name`` set to | metrics will be tagged with ``state_type: sls`` and ``state_name`` set to | |||
the name of the state that was invoked, e.g. ``role_salt_master.netapi``. | the name of the state that was invoked, e.g. ``role_salt_master.netapi``. | |||
""" | """ | |||
# Import python libs | ||||
from __future__ import absolute_import, print_function, unicode_literals | ||||
import logging | import logging | |||
import salt.returners | import salt.returners | |||
# Import Salt libs | ||||
import salt.utils.jid | import salt.utils.jid | |||
# Import third party libs | ||||
try: | try: | |||
import appoptics_metrics | import appoptics_metrics | |||
HAS_APPOPTICS = True | HAS_APPOPTICS = True | |||
except ImportError: | except ImportError: | |||
HAS_APPOPTICS = False | HAS_APPOPTICS = False | |||
# Define the module's Virtual Name | # Define the module's Virtual Name | |||
__virtualname__ = "appoptics" | __virtualname__ = "appoptics" | |||
log = logging.getLogger(__name__) | log = logging.getLogger(__name__) | |||
def __virtual__(): | def __virtual__(): | |||
if not HAS_APPOPTICS: | if not HAS_APPOPTICS: | |||
log.error( | ||||
"The appoptics_return module couldn't load the appoptics_metrics mod | ||||
ule." | ||||
) | ||||
log.error("please make sure it is installed and is in the PYTHON_PATH.") | ||||
return ( | return ( | |||
False, | False, | |||
"Could not import appoptics_metrics module; " | "Could not import appoptics_metrics module; " | |||
"appoptics-metrics python client is not installed.", | "appoptics-metrics python client is not installed.", | |||
) | ) | |||
return __virtualname__ | return __virtualname__ | |||
def _get_options(ret=None): | def _get_options(ret=None): | |||
""" | """ | |||
Get the appoptics options from salt. | Get the appoptics options from salt. | |||
skipping to change at line 126 | skipping to change at line 115 | |||
_options = salt.returners.get_returner_options( | _options = salt.returners.get_returner_options( | |||
__virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ | __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ | |||
) | ) | |||
_options["api_url"] = _options.get("api_url", "api.appoptics.com") | _options["api_url"] = _options.get("api_url", "api.appoptics.com") | |||
_options["sls_states"] = _options.get("sls_states", []) | _options["sls_states"] = _options.get("sls_states", []) | |||
_options["tags"] = _options.get( | _options["tags"] = _options.get( | |||
"tags", {"host_hostname_alias": __salt__["grains.get"]("id")} | "tags", {"host_hostname_alias": __salt__["grains.get"]("id")} | |||
) | ) | |||
log.debug("Retrieved appoptics options: {}".format(_options)) | log.debug("Retrieved appoptics options: %s", _options) | |||
return _options | return _options | |||
def _get_appoptics(options): | def _get_appoptics(options): | |||
""" | """ | |||
Return an appoptics connection object. | Return an appoptics connection object. | |||
""" | """ | |||
conn = appoptics_metrics.connect( | conn = appoptics_metrics.connect( | |||
options.get("api_token"), | options.get("api_token"), | |||
sanitizer=appoptics_metrics.sanitize_metric_name, | sanitizer=appoptics_metrics.sanitize_metric_name, | |||
hostname=options.get("api_url"), | hostname=options.get("api_url"), | |||
skipping to change at line 155 | skipping to change at line 144 | |||
if isinstance(resultset, dict) and "duration" in resultset: | if isinstance(resultset, dict) and "duration" in resultset: | |||
# Count the pass vs failures | # Count the pass vs failures | |||
if resultset["result"]: | if resultset["result"]: | |||
results["num_passed_states"] += 1 | results["num_passed_states"] += 1 | |||
else: | else: | |||
results["num_failed_states"] += 1 | results["num_failed_states"] += 1 | |||
# Count durations | # Count durations | |||
results["runtime"] += resultset["duration"] | results["runtime"] += resultset["duration"] | |||
log.debug("Parsed state metrics: {}".format(results)) | log.debug("Parsed state metrics: %s", results) | |||
return results | return results | |||
def _state_metrics(ret, options, tags): | def _state_metrics(ret, options, tags): | |||
# Calculate the runtimes and number of failed states. | # Calculate the runtimes and number of failed states. | |||
stats = _calculate_runtimes(ret["return"]) | stats = _calculate_runtimes(ret["return"]) | |||
log.debug("Batching Metric retcode with {}".format(ret["retcode"])) | log.debug("Batching Metric retcode with %s", ret["retcode"]) | |||
appoptics_conn = _get_appoptics(options) | appoptics_conn = _get_appoptics(options) | |||
q = appoptics_conn.new_queue(tags=tags) | q = appoptics_conn.new_queue(tags=tags) | |||
q.add("saltstack.retcode", ret["retcode"]) | q.add("saltstack.retcode", ret["retcode"]) | |||
log.debug( | log.debug("Batching Metric num_failed_jobs with %s", stats["num_failed_state | |||
"Batching Metric num_failed_jobs with {}".format(stats["num_failed_state | s"]) | |||
s"]) | ||||
) | ||||
q.add("saltstack.failed", stats["num_failed_states"]) | q.add("saltstack.failed", stats["num_failed_states"]) | |||
log.debug( | log.debug("Batching Metric num_passed_states with %s", stats["num_passed_sta | |||
"Batching Metric num_passed_states with {}".format(stats["num_passed_sta | tes"]) | |||
tes"]) | ||||
) | ||||
q.add("saltstack.passed", stats["num_passed_states"]) | q.add("saltstack.passed", stats["num_passed_states"]) | |||
log.debug("Batching Metric runtime with {}".format(stats["runtime"])) | log.debug("Batching Metric runtime with %s".stats["runtime"]) | |||
q.add("saltstack.runtime", stats["runtime"]) | q.add("saltstack.runtime", stats["runtime"]) | |||
log.debug( | log.debug( | |||
"Batching with Metric total states {}".format( | "Batching with Metric total states %s", | |||
(stats["num_failed_states"] + stats["num_passed_states"]) | stats["num_failed_states"] + stats["num_passed_states"], | |||
) | ||||
) | ) | |||
q.add( | q.add( | |||
"saltstack.highstate.total_states", | "saltstack.highstate.total_states", | |||
(stats["num_failed_states"] + stats["num_passed_states"]), | (stats["num_failed_states"] + stats["num_passed_states"]), | |||
) | ) | |||
log.info("Sending metrics to appoptics.") | log.info("Sending metrics to appoptics.") | |||
q.submit() | q.submit() | |||
def returner(ret): | def returner(ret): | |||
""" | """ | |||
skipping to change at line 206 | skipping to change at line 190 | |||
the result of that state if it's present. | the result of that state if it's present. | |||
""" | """ | |||
options = _get_options(ret) | options = _get_options(ret) | |||
states_to_report = ["state.highstate"] | states_to_report = ["state.highstate"] | |||
if options.get("sls_states"): | if options.get("sls_states"): | |||
states_to_report.append("state.sls") | states_to_report.append("state.sls") | |||
if ret["fun"] in states_to_report: | if ret["fun"] in states_to_report: | |||
tags = options.get("tags", {}).copy() | tags = options.get("tags", {}).copy() | |||
tags["state_type"] = ret["fun"] | tags["state_type"] = ret["fun"] | |||
log.info("Tags for this run are {}".format(str(tags))) | log.info("Tags for this run are %s", str(tags)) | |||
matched_states = set(ret["fun_args"]).intersection( | matched_states = set(ret["fun_args"]).intersection( | |||
set(options.get("sls_states", [])) | set(options.get("sls_states", [])) | |||
) | ) | |||
# What can I do if a run has multiple states that match? | # What can I do if a run has multiple states that match? | |||
# In the mean time, find one matching state name and use it. | # In the mean time, find one matching state name and use it. | |||
if matched_states: | if matched_states: | |||
tags["state_name"] = sorted(matched_states)[0] | tags["state_name"] = sorted(matched_states)[0] | |||
log.debug("Found returned data from {}.".format(tags["state_name"])) | log.debug("Found returned data from %s.", tags["state_name"]) | |||
_state_metrics(ret, options, tags) | _state_metrics(ret, options, tags) | |||
End of changes. 14 change blocks. | ||||
29 lines changed or deleted | 12 lines changed or added |