1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# Copyright: Ansible Project 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9DOCUMENTATION = ''' 10module: statsd 11short_description: Send metrics to StatsD 12version_added: 2.1.0 13description: 14 - The C(statsd) module sends metrics to StatsD. 15 - For more information, see U(https://statsd-metrics.readthedocs.io/en/latest/). 16 - Supported metric types are C(counter) and C(gauge). 17 Currently unupported metric types are C(timer), C(set), and C(gaugedelta). 18author: "Mark Mercado (@mamercad)" 19requirements: 20 - statsd 21options: 22 state: 23 type: str 24 description: 25 - State of the check, only C(present) makes sense. 26 choices: ["present"] 27 default: present 28 host: 29 type: str 30 default: localhost 31 description: 32 - StatsD host (hostname or IP) to send metrics to. 33 port: 34 type: int 35 default: 8125 36 description: 37 - The port on C(host) which StatsD is listening on. 38 protocol: 39 type: str 40 default: udp 41 choices: ["udp", "tcp"] 42 description: 43 - The transport protocol to send metrics over. 44 timeout: 45 type: float 46 default: 1.0 47 description: 48 - Sender timeout, only applicable if C(protocol) is C(tcp). 49 metric: 50 type: str 51 required: true 52 description: 53 - The name of the metric. 54 metric_type: 55 type: str 56 required: true 57 choices: ["counter", "gauge"] 58 description: 59 - The type of metric. 60 metric_prefix: 61 type: str 62 description: 63 - The prefix to add to the metric. 64 value: 65 type: int 66 required: true 67 description: 68 - The value of the metric. 69 delta: 70 type: bool 71 default: false 72 description: 73 - If the metric is of type C(gauge), change the value by C(delta). 74''' 75 76EXAMPLES = ''' 77- name: Increment the metric my_counter by 1 78 community.general.statsd: 79 host: localhost 80 port: 9125 81 protocol: tcp 82 metric: my_counter 83 metric_type: counter 84 value: 1 85 86- name: Set the gauge my_gauge to 7 87 community.general.statsd: 88 host: localhost 89 port: 9125 90 protocol: tcp 91 metric: my_gauge 92 metric_type: gauge 93 value: 7 94''' 95 96 97from ansible.module_utils.basic import (AnsibleModule, missing_required_lib) 98 99try: 100 from statsd import StatsClient, TCPStatsClient 101 HAS_STATSD = True 102except ImportError: 103 HAS_STATSD = False 104 105 106def udp_statsd_client(**client_params): 107 return StatsClient(**client_params) 108 109 110def tcp_statsd_client(**client_params): 111 return TCPStatsClient(**client_params) 112 113 114def main(): 115 116 module = AnsibleModule( 117 argument_spec=dict( 118 state=dict(type='str', default='present', choices=['present']), 119 host=dict(type='str', default='localhost'), 120 port=dict(type='int', default=8125), 121 protocol=dict(type='str', default='udp', choices=['udp', 'tcp']), 122 timeout=dict(type='float', default=1.0), 123 metric=dict(type='str', required=True), 124 metric_type=dict(type='str', required=True, choices=['counter', 'gauge']), 125 metric_prefix=dict(type='str', default=''), 126 value=dict(type='int', required=True), 127 delta=dict(type='bool', default=False), 128 ), 129 supports_check_mode=False 130 ) 131 132 if not HAS_STATSD: 133 module.fail_json(msg=missing_required_lib('statsd')) 134 135 host = module.params.get('host') 136 port = module.params.get('port') 137 protocol = module.params.get('protocol') 138 timeout = module.params.get('timeout') 139 metric = module.params.get('metric') 140 metric_type = module.params.get('metric_type') 141 metric_prefix = module.params.get('metric_prefix') 142 value = module.params.get('value') 143 delta = module.params.get('delta') 144 145 if protocol == 'udp': 146 client = udp_statsd_client(host=host, port=port, prefix=metric_prefix, maxudpsize=512, ipv6=False) 147 elif protocol == 'tcp': 148 client = tcp_statsd_client(host=host, port=port, timeout=timeout, prefix=metric_prefix, ipv6=False) 149 150 metric_name = '%s/%s' % (metric_prefix, metric) if metric_prefix else metric 151 metric_display_value = '%s (delta=%s)' % (value, delta) if metric_type == 'gauge' else value 152 153 try: 154 if metric_type == 'counter': 155 client.incr(metric, value) 156 elif metric_type == 'gauge': 157 client.gauge(metric, value, delta=delta) 158 159 except Exception as exc: 160 module.fail_json(msg='Failed sending to StatsD %s' % str(exc)) 161 162 finally: 163 if protocol == 'tcp': 164 client.close() 165 166 module.exit_json(msg="Sent %s %s -> %s to StatsD" % (metric_type, metric_name, str(metric_display_value)), changed=True) 167 168 169if __name__ == '__main__': 170 main() 171