add memcached support
paulfantom committed
Aug 2, 2016 at 20:36 UTC
b999d13c044214dab821306a71c04afa9857c654
4 files changed
+247
conf.d/Makefile.am
+1
@@ -25,6 +25,7 @@ dist_pythonconfig_DATA = \
25
python.d/example.conf \
26
python.d/exim.conf \
27
python.d/hddtemp.conf \
28
+ python.d/memcached.conf \
29
python.d/mysql.conf \
30
python.d/nginx.conf \
31
python.d/phpfpm.conf \
conf.d/python.d/memcached.conf
new
+85
@@ -0,0 +1,85 @@
1
+# netdata python.d.plugin configuration for memcached
2
+#
3
+# This file is in YaML format. Generally the format is:
4
+#
5
+# name: value
6
+#
7
+# There are 2 sections:
8
+# - global variables
9
+# - one or more JOBS
10
+#
11
+# JOBS allow you to collect values from multiple sources.
12
+# Each source will have its own set of charts.
13
+#
14
+# JOB parameters have to be indented (using spaces only, example below).
15
+
16
+# ----------------------------------------------------------------------
17
+# Global Variables
18
+# These variables set the defaults for all JOBs, however each JOB
19
+# may define its own, overriding the defaults.
20
+
21
+# update_every sets the default data collection frequency.
22
+# If unset, the python.d.plugin default is used.
23
+# update_every: 1
24
+
25
+# priority controls the order of charts at the netdata dashboard.
26
+# Lower numbers move the charts towards the top of the page.
27
+# If unset, the default for python.d.plugin is used.
28
+# priority: 60000
29
+
30
+# retries sets the number of retries to be made in case of failures.
31
+# If unset, the default for python.d.plugin is used.
32
+# Attempts to restore the service are made once every update_every
33
+# and only if the module has collected values in the past.
34
+# retries: 5
35
+
36
+# ----------------------------------------------------------------------
37
+# JOBS (data collection sources)
38
+#
39
+# The default JOBS share the same *name*. JOBS with the same name
40
+# are mutually exclusive. Only one of them will be allowed running at
41
+# any time. This allows autodetection to try several alternatives and
42
+# pick the one that works.
43
+#
44
+# Any number of jobs is supported.
45
+#
46
+# All python.d.plugin JOBS (for all its modules) support a set of
47
+# predefined parameters. These are:
48
+#
49
+# job_name:
50
+# name: myname # the JOB's name as it will appear at the
51
+# # dashboard (by default is the job_name)
52
+# # JOBs sharing a name are mutually exclusive
53
+# update_every: 1 # the JOB's data collection frequency
54
+# priority: 60000 # the JOB's order on the dashboard
55
+# retries: 5 # the JOB's number of restoration attempts
56
+#
57
+# Additionally to the above, memcached also supports the following:
58
+#
59
+# socket: 'path/to/memcached.sock'
60
+#
61
+# or
62
+# host: 'IP or HOSTNAME' # the host to connect to
63
+# port: PORT # the port to connect to
64
+#
65
+#
66
+
67
+# ----------------------------------------------------------------------
68
+# AUTO-DETECTION JOBS
69
+# only one of them will run (they have the same name)
70
+
71
+localhost:
72
+ name : 'local'
73
+ host : 'localhost'
74
+ port : 11211
75
+
76
+localipv4:
77
+ name : 'local'
78
+ host : '127.0.0.1'
79
+ port : 11211
80
+
81
+localipv6:
82
+ name : 'local'
83
+ host : '::1'
84
+ port : 11211
85
+
python.d/Makefile.am
+1
@@ -14,6 +14,7 @@ dist_python_SCRIPTS = \
14
example.chart.py \
15
exim.chart.py \
16
hddtemp.chart.py \
17
+ memcached.chart.py \
18
mysql.chart.py \
19
nginx.chart.py \
20
phpfpm.chart.py \
python.d/memcached.chart.py
new
+160
@@ -0,0 +1,160 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: memcached netdata python.d module
3
+# Author: Pawel Krupa (paulfantom)
4
+
5
+from base import SocketService
6
+
7
+# default module values (can be overridden per job in `config`)
8
+#update_every = 2
9
+priority = 60000
10
+retries = 60
11
+
12
+# default job configuration (overridden by python.d.plugin)
13
+# config = {'local': {
14
+# 'update_every': update_every,
15
+# 'retries': retries,
16
+# 'priority': priority,
17
+# 'host': 'localhost',
18
+# 'port': 11211,
19
+# 'unix_socket': None
20
+# }}
21
+
22
+ORDER = ['net', 'connections', 'items', 'evicted_reclaimed', 'get', 'get_rate', 'set_rate', 'delete', 'cas', 'increment', 'decrement', 'touch', 'touch_rate']
23
+
24
+CHARTS = {
25
+ 'net': {
26
+ 'options': [None, 'Network', 'bytes', 'Network', 'memcached.net', 'line'],
27
+ 'lines': [
28
+ ['bytes_read', 'read', 'absolute'],
29
+ ['bytes_written', 'written', 'absolute']
30
+ ]},
31
+ 'connections': {
32
+ 'options': [None, 'Connections', 'connections', 'Cluster', 'memcached.cluster', 'line'],
33
+ 'lines': [
34
+ ['curr_connections', 'current', 'absolute'],
35
+ ['rejected_connections', 'rejected', 'absolute'],
36
+ ['total_connections', 'total', 'absolute']
37
+ ]},
38
+ 'items': {
39
+ 'options': [None, 'Items', 'items', 'Cluster', 'memcached.cluster', 'line'],
40
+ 'lines': [
41
+ ['curr_items', 'current', 'absolute'],
42
+ ['total_items', 'total', 'absolute']
43
+ ]},
44
+ 'evicted_reclaimed': {
45
+ 'options': [None, 'Items', 'items', 'Evicted & Reclaimed', 'memcached.evicted_reclaimed', 'line'],
46
+ 'lines': [
47
+ ['evictions', 'evicted', 'absolute'],
48
+ ['reclaimed', 'reclaimed', 'absolute']
49
+ ]},
50
+ 'get': {
51
+ 'options': [None, 'Requests', 'requests', 'GET', 'memcached.get', 'stacked'],
52
+ 'lines': [
53
+ ['get_hits', 'hits', 'percent-of-absolute-row'],
54
+ ['get_misses', 'misses', 'percent-of-absolute-row']
55
+ ]},
56
+ 'get_rate': {
57
+ 'options': [None, 'Rate', 'requests/s', 'GET', 'memcached.get', 'line'],
58
+ 'lines': [
59
+ ['cmd_get', 'rate', 'incremental']
60
+ ]},
61
+ 'set_rate': {
62
+ 'options': [None, 'Rate', 'requests/s', 'SET', 'memcached.set', 'line'],
63
+ 'lines': [
64
+ ['cmd_set', 'rate', 'incremental']
65
+ ]},
66
+ 'delete': {
67
+ 'options': [None, 'Requests', 'requests', 'DELETE', 'memcached.delete', 'stacked'],
68
+ 'lines': [
69
+ ['delete_hits', 'hits', 'percent-of-absolute-row'],
70
+ ['delete_misses', 'misses', 'percent-of-absolute-row'],
71
+ ]},
72
+ 'cas': {
73
+ 'options': [None, 'Requests', 'requests', 'CAS', 'memcached.cas', 'stacked'],
74
+ 'lines': [
75
+ ['cas_hits', 'hits', 'percent-of-absolute-row'],
76
+ ['cas_misses', 'misses', 'percent-of-absolute-row'],
77
+ ['cas_badval', 'bad value', 'percent-of-absolute-row']
78
+ ]},
79
+ 'increment': {
80
+ 'options': [None, 'Requests', 'requests', 'Increment', 'memcached.incr', 'stacked'],
81
+ 'lines': [
82
+ ['incr_hits', 'hits', 'percent-of-absolute-row'],
83
+ ['incr_misses', 'misses', 'percent-of-absolute-row']
84
+ ]},
85
+ 'decrement': {
86
+ 'options': [None, 'Requests', 'requests', 'Decrement', 'memcached.decr', 'stacked'],
87
+ 'lines': [
88
+ ['decr_hits', 'hits', 'percent-of-absolute-row'],
89
+ ['decr_misses', 'misses', 'percent-of-absolute-row']
90
+ ]},
91
+ 'touch': {
92
+ 'options': [None, 'Requests', 'requests', 'Touch', 'memcached.touch', 'stacked'],
93
+ 'lines': [
94
+ ['touch_hits', 'hits', 'percent-of-absolute-row'],
95
+ ['touch_misses', 'misses', 'percent-of-absolute-row']
96
+ ]},
97
+ 'touch_rate': {
98
+ 'options': [None, 'Rate', 'requests/s', 'Touch', 'memcached.touch', 'line'],
99
+ 'lines': [
100
+ ['cmd_touch', 'rate', 'incremental']
101
+ ]}
102
+}
103
+
104
+
105
+class Service(SocketService):
106
+ def __init__(self, configuration=None, name=None):
107
+ SocketService.__init__(self, configuration=configuration, name=name)
108
+ self.request = "stats\r\n"
109
+ self.host = "localhost"
110
+ self.port = 11211
111
+ self.unix_socket = None
112
+ self.order = ORDER
113
+ self.definitions = CHARTS
114
+
115
+ def _get_data(self):
116
+ """
117
+ Get data from socket
118
+ :return: dict
119
+ """
120
+ try:
121
+ raw = self._get_raw_data().split("\n")
122
+ except AttributeError:
123
+ self.error("no data received")
124
+ return None
125
+ if raw[0].startswith('ERROR'):
126
+ self.error("Memcached returned ERROR")
127
+ return None
128
+ data = {}
129
+ for line in raw:
130
+ if line.startswith('STAT'):
131
+ try:
132
+ t = line[5:].split(' ')
133
+ data[t[0]] = int(t[1])
134
+ except (IndexError, ValueError):
135
+ pass
136
+ try:
137
+ data['hit_rate'] = int((data['keyspace_hits'] / float(data['keyspace_hits'] + data['keyspace_misses'])) * 100)
138
+ except:
139
+ data['hit_rate'] = 0
140
+
141
+ if len(data) == 0:
142
+ self.error("received data doesn't have needed records")
143
+ return None
144
+ else:
145
+ return data
146
+
147
+ def check(self):
148
+ """
149
+ Parse configuration, check if memcached is available
150
+ :return: boolean
151
+ """
152
+ self._parse_config()
153
+ if self.name == "":
154
+ self.name = "local"
155
+ self.chart_name += "_" + self.name
156
+ data = self._get_data()
157
+ if data is None:
158
+ return False
159
+
160
+ return True