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