varnish module rewriting
lgz committed
Dec 1, 2017 at 00:15 UTC
6ebe39df4d1cdc0934eb0f09b14f1dca174c750e
1 file changed
+182
-184
python.d/varnish.chart.py
+182
-184
@@ -3,241 +3,239 @@
3
# Author: l2isbad
4
5
import re
6
-from subprocess import Popen, PIPE
6
7
from bases.collection import find_binary
9
-from bases.FrameworkServices.SimpleService import SimpleService
8
+from bases.FrameworkServices.ExecutableService import ExecutableService
9
10
# default module values (can be overridden per job in `config`)
11
# update_every = 2
12
priority = 60000
13
retries = 60
14
16
-ORDER = ['session', 'hit_rate', 'chit_rate', 'expunge', 'threads', 'backend_health', 'memory_usage', 'bad', 'uptime']
15
+ORDER = ['session_connections', 'client_requests',
16
+ 'all_time_hit_rate', 'current_poll_hit_rate', 'cached_objects_expired', 'cached_objects_nuked',
17
+ 'threads_total', 'threads_statistics', 'threads_queue_len',
18
+ 'backend_connections', 'backend_requests',
19
+ 'esi_statistics',
20
+ 'memory_usage',
21
+ 'uptime']
22
23
CHARTS = {
19
- 'backend_health': {
24
+ 'session_connections': {
25
+ 'options': [None, 'Connections Statistics', 'connection/s',
26
+ 'client metrics', 'varnish.session_connection', 'line'],
27
'lines': [
21
- ['backend_conn', 'conn', 'incremental', 1, 1],
22
- ['backend_unhealthy', 'unhealthy', 'incremental', 1, 1],
23
- ['backend_busy', 'busy', 'incremental', 1, 1],
24
- ['backend_fail', 'fail', 'incremental', 1, 1],
25
- ['backend_reuse', 'reuse', 'incremental', 1, 1],
26
- ['backend_recycle', 'resycle', 'incremental', 1, 1],
27
- ['backend_toolate', 'toolate', 'incremental', 1, 1],
28
- ['backend_retry', 'retry', 'incremental', 1, 1],
29
- ['backend_req', 'req', 'incremental', 1, 1]],
30
- 'options': [None, 'Backend Health', 'connections/s', 'backend health', 'varnish.backend_health', 'line']
28
+ ['sess_conn', 'accepted', 'incremental'],
29
+ ['sess_dropped', 'dropped', 'incremental']
30
+ ]
31
},
32
- 'bad': {
32
+ 'client_requests': {
33
+ 'options': [None, 'Received Client Requests', 'requests/s',
34
+ 'client metrics', 'varnish.client_requests', 'line'],
35
'lines': [
34
- ['sess_drop_b', None, 'incremental', 1, 1],
35
- ['backend_unhealthy_b', None, 'incremental', 1, 1],
36
- ['fetch_failed', None, 'incremental', 1, 1],
37
- ['backend_busy_b', None, 'incremental', 1, 1],
38
- ['threads_failed_b', None, 'incremental', 1, 1],
39
- ['threads_limited_b', None, 'incremental', 1, 1],
40
- ['threads_destroyed_b', None, 'incremental', 1, 1],
41
- ['thread_queue_len_b', 'queue_len', 'absolute', 1, 1],
42
- ['losthdr_b', None, 'incremental', 1, 1],
43
- ['esi_errors_b', None, 'incremental', 1, 1],
44
- ['esi_warnings_b', None, 'incremental', 1, 1],
45
- ['sess_fail_b', None, 'incremental', 1, 1],
46
- ['sc_pipe_overflow_b', None, 'incremental', 1, 1],
47
- ['sess_pipe_overflow_b', None, 'incremental', 1, 1]],
48
- 'options': [None, 'Misbehavior', 'problems/s', 'problems summary', 'varnish.bad', 'line']
36
+ ['client_req', 'received', 'incremental']
37
+ ]
38
},
50
- 'expunge': {
39
+ 'all_time_hit_rate': {
40
+ 'options': [None, 'All History Hit Rate Ratio', 'percent', 'cache performance',
41
+ 'varnish.all_time_hit_rate', 'stacked'],
42
'lines': [
52
- ['n_expired', 'expired', 'incremental', 1, 1],
53
- ['n_lru_nuked', 'lru_nuked', 'incremental', 1, 1]],
54
- 'options': [None, 'Object expunging', 'objects/s', 'cache performance', 'varnish.expunge', 'line']
43
+ ['cache_hit', 'hit', 'percentage-of-absolute-row'],
44
+ ['cache_miss', 'miss', 'percentage-of-absolute-row'],
45
+ ['cache_hitpass', 'hitpass', 'percentage-of-absolute-row']]
46
},
56
- 'hit_rate': {
47
+ 'current_poll_hit_rate': {
48
+ 'options': [None, 'Current Poll Hit Rate Ratio', 'percent', 'cache performance',
49
+ 'varnish.current_poll_hit_rate', 'stacked'],
50
'lines': [
58
- ['cache_hit_perc', 'hit', 'absolute', 1, 100],
59
- ['cache_miss_perc', 'miss', 'absolute', 1, 100],
60
- ['cache_hitpass_perc', 'hitpass', 'absolute', 1, 100]],
61
- 'options': [None, 'All History Hit Rate Ratio', 'percent', 'cache performance', 'varnish.hit_rate', 'stacked']
51
+ ['cache_hit', 'hit', 'percentage-of-incremental-row'],
52
+ ['cache_miss', 'miss', 'percentage-of-incremental-row'],
53
+ ['cache_hitpass', 'hitpass', 'percentage-of-incremental-row']
54
+ ]
55
},
63
- 'chit_rate': {
56
+ 'cached_objects_expired': {
57
+ 'options': [None, 'Expired Objects', 'objects/s', 'cache performance',
58
+ 'varnish.cached_objects_expired', 'line'],
59
'lines': [
65
- ['cache_hit_cperc', 'hit', 'absolute', 1, 100],
66
- ['cache_miss_cperc', 'miss', 'absolute', 1, 100],
67
- ['cache_hitpass_cperc', 'hitpass', 'absolute', 1, 100]],
68
- 'options': [None, 'Current Poll Hit Rate Ratio', 'percent', 'cache performance', 'varnish.chit_rate', 'stacked']
60
+ ['n_expired', 'expired', 'incremental']
61
+ ]
62
},
70
- 'memory_usage': {
63
+ 'cached_objects_nuked': {
64
+ 'options': [None, 'Least Recently Used Nuked Objects', 'objects/s', 'cache performance',
65
+ 'varnish.cached_objects_nuked', 'line'],
66
+ 'lines': [
67
+ ['n_lru_nuked', 'nuked', 'incremental']
68
+ ]
69
+ },
70
+ 'threads_total': {
71
+ 'options': [None, 'Number Of Threads In All Pools', 'number', 'thread related metrics',
72
+ 'varnish.threads_total', 'line'],
73
+ 'lines': [
74
+ ['threads', None, 'absolute']
75
+ ]
76
+ },
77
+ 'threads_statistics': {
78
+ 'options': [None, 'Threads Statistics', 'threads/s', 'thread related metrics',
79
+ 'varnish.threads_statistics', 'line'],
80
+ 'lines': [
81
+ ['threads_created', 'created', 'incremental'],
82
+ ['threads_failed', 'failed', 'incremental'],
83
+ ['threads_limited', 'limited', 'incremental']
84
+ ]
85
+ },
86
+ 'threads_queue_len': {
87
+ 'options': [None, 'Current Queue Length', 'requests', 'thread related metrics',
88
+ 'varnish.threads_queue_len', 'line'],
89
+ 'lines': [
90
+ ['thread_queue_len', 'in queue']
91
+ ]
92
+ },
93
+ 'backend_connections': {
94
+ 'options': [None, 'Backend Connections Statistics', 'connections/s', 'backend metrics',
95
+ 'varnish.backend_connections', 'line'],
96
'lines': [
72
- ['s0.g_space', 'available', 'absolute', 1, 1 << 20],
73
- ['s0.g_bytes', 'allocated', 'absolute', -1, 1 << 20]],
74
- 'options': [None, 'Memory Usage', 'megabytes', 'memory usage', 'varnish.memory_usage', 'stacked']
97
+ ['backend_conn', 'successful', 'incremental'],
98
+ ['backend_unhealthy', 'unhealthy', 'incremental'],
99
+ ['backend_reuse', 'reused', 'incremental'],
100
+ ['backend_toolate', 'closed', 'incremental'],
101
+ ['backend_recycle', 'resycled', 'incremental'],
102
+ ['backend_fail', 'failed', 'incremental']
103
+ ]
104
},
76
- 'session': {
105
+ 'backend_requests': {
106
+ 'options': [None, 'Requests To The Backend', 'requests/s', 'backend metrics',
107
+ 'varnish.backend_requests', 'line'],
108
'lines': [
78
- ['sess_conn', 'sess_conn', 'incremental', 1, 1],
79
- ['client_req', 'client_requests', 'incremental', 1, 1],
80
- ['client_conn', 'client_conn', 'incremental', 1, 1],
81
- ['client_drop', 'client_drop', 'incremental', 1, 1],
82
- ['sess_dropped', 'sess_dropped', 'incremental', 1, 1]],
83
- 'options': [None, 'Sessions', 'units/s', 'client metrics', 'varnish.session', 'line']
109
+ ['backend_req', 'requests', 'incremental']
110
+ ]
111
},
85
- 'threads': {
112
+ 'esi_statistics': {
113
+ 'options': [None, 'ESI Statistics', 'problems/s', 'esi related metrics', 'varnish.esi_statistics', 'line'],
114
'lines': [
87
- ['threads', None, 'absolute', 1, 1],
88
- ['threads_created', 'created', 'incremental', 1, 1],
89
- ['threads_failed', 'failed', 'incremental', 1, 1],
90
- ['threads_limited', 'limited', 'incremental', 1, 1],
91
- ['thread_queue_len', 'queue_len', 'incremental', 1, 1],
92
- ['sess_queued', 'sess_queued', 'incremental', 1, 1]],
93
- 'options': [None, 'Thread Status', 'threads/s', 'thread related metrics', 'varnish.threads', 'line']
115
+ ['esi_errors', 'errors', 'incremental'],
116
+ ['esi_warnings', 'warnings', 'incremental']
117
+ ]
118
+ },
119
+ 'memory_usage': {
120
+ 'options': [None, 'Memory Usage', 'MB', 'memory usage', 'varnish.memory_usage', 'stacked'],
121
+ 'lines': [
122
+ ['memory_free', 'free', 'absolute', 1, 1 << 20],
123
+ ['memory_allocated', 'allocated', 'absolute', 1, 1 << 20]]
124
},
125
'uptime': {
126
'lines': [
97
- ['uptime', None, 'absolute', 1, 1]
127
+ ['uptime', None, 'absolute']
128
],
129
'options': [None, 'Uptime', 'seconds', 'uptime', 'varnish.uptime', 'line']
130
}
131
}
132
133
104
-class Service(SimpleService):
134
+class Parser:
135
+ _backend_new = re.compile(r'VBE.([\d\w_.]+)\(.*?\).(beresp[\w_]+)\s+(\d+)')
136
+ _backend_old = re.compile(r'VBE\.[\d\w-]+\.([\w\d_]+).(beresp[\w_]+)\s+(\d+)')
137
+ _default = re.compile(r'([A-Z]+\.)?([\d\w_.]+)\s+(\d+)')
138
+
139
+ def __init__(self):
140
+ self.re_default = None
141
+ self.re_backend = None
142
+
143
+ def init(self, data):
144
+ data = ''.join(data)
145
+ parsed_main = Parser._default.findall(data)
146
+ if parsed_main:
147
+ self.re_default = Parser._default
148
+
149
+ parsed_backend = Parser._backend_new.findall(data)
150
+ if parsed_backend:
151
+ self.re_backend = Parser._backend_new
152
+ else:
153
+ parsed_backend = Parser._backend_old.findall(data)
154
+ if parsed_backend:
155
+ self.re_backend = Parser._backend_old
156
+
157
+ def server_stats(self, data):
158
+ return self.re_default.findall(''.join(data))
159
+
160
+ def backend_stats(self, data):
161
+ return self.re_backend.findall(''.join(data))
162
+
163
+
164
+class Service(ExecutableService):
165
def __init__(self, configuration=None, name=None):
106
- SimpleService.__init__(self, configuration=configuration, name=name)
107
- self.varnish = find_binary('varnishstat')
108
- self.order = ORDER[:]
109
- self.definitions = dict(CHARTS)
110
- self.regex_all = re.compile(r'([A-Z]+\.)?([\d\w_.]+)\s+(\d+)')
111
- self.regex_backend = None
112
- self.cache_prev = list()
113
- self.backend_list = list()
166
+ ExecutableService.__init__(self, configuration=configuration, name=name)
167
+ self.order = ORDER
168
+ self.definitions = CHARTS
169
+ varnishstat = find_binary('varnishstat')
170
+ self.command = [varnishstat, '-1'] if varnishstat else None
171
+ self.parser = Parser()
172
173
def check(self):
116
- # Cant start without 'varnishstat' command
117
- if not self.varnish:
118
- self.error('Can\'t locate \'varnishstat\' binary or binary is not executable by netdata')
174
+ if not self.command:
175
+ self.error("Can't locate 'varnishstat' binary or binary is not executable by user netdata")
176
return False
177
121
- # If command is present and we can execute it we need to make sure..
122
- # 1. STDOUT is not empty
178
+ # STDOUT is not empty
179
reply = self._get_raw_data()
180
if not reply:
125
- self.error("No output from 'varnishstat' (not enough privileges?)")
126
- return False
127
-
128
- # 2. Output is parsable (list is not empty after regex findall)
129
- found = self.regex_all.findall(reply)
130
- if not found:
131
- self.error('Cant parse output...')
181
+ self.error("No output from 'varnishstat'. Not enough privileges?")
182
return False
183
134
- # We need to find the right regex for backend parse
135
- # Could be
136
- # VBE.boot.super_backend.pipe_hdrbyte (new)
137
- # or
138
- # VBE.default2(127.0.0.2,,81).bereq_bodybytes (old)
139
- # Regex result: [('super_backend', 'beresp_hdrbytes', '0'), ('super_backend', 'beresp_bodybytes', '0')]
140
-
141
- regex1 = re.compile(r'VBE.([\d\w_.]+)\(.*?\).(beresp[\w_]+)\s+(\d+)')
142
- regex2 = re.compile(r'VBE\.[\d\w-]+\.([\w\d_]+).(beresp[\w_]+)\s+(\d+)')
184
+ self.parser.init(''.join(reply))
185
144
- self.backend_list = regex1.findall(reply)[::2]
145
- if self.backend_list:
146
- self.regex_backend = regex1
147
- else:
148
- self.backend_list = regex2.findall(reply)[::2]
149
- self.regex_backend = regex2
186
+ # Output is parsable
187
+ if not self.parser.re_default:
188
+ self.error('Cant parse the output...')
189
+ return False
190
151
- self.create_charts()
191
+ if self.parser.re_backend:
192
+ backends = [b[0] for b in self.parser.backend_stats(reply)[::2]]
193
+ self.create_backends_charts(backends)
194
return True
195
154
- def _get_raw_data(self):
155
- try:
156
- reply = Popen([self.varnish, '-1'], stdout=PIPE, stderr=PIPE, shell=False)
157
- except OSError:
158
- return None
159
-
160
- raw_data = reply.communicate()[0]
161
-
162
- if not raw_data:
163
- return None
164
-
165
- return raw_data.decode()
166
-
167
- def _get_data(self):
196
+ def get_data(self):
197
"""
198
Format data received from shell command
199
:return: dict
200
"""
172
- raw_data = self._get_raw_data()
173
- data_all = self.regex_all.findall(raw_data)
174
- data_backend = self.regex_backend.findall(raw_data)
201
+ raw = self._get_raw_data()
202
+ if not raw:
203
+ return None
204
176
- if not data_all:
205
+ data = dict()
206
+ server_stats = self.parser.server_stats(raw)
207
+ if not server_stats:
208
return None
209
179
- # 1. ALL data from 'varnishstat -1'. t - type(MAIN, MEMPOOL etc)
180
- to_netdata = dict((k, int(v)) for t, k, v in data_all)
181
-
182
- # 2. ADD backend statistics
183
- to_netdata.update(dict(('_'.join([n, k]), int(v)) for n, k, v in data_backend))
184
-
185
- # 3. ADD additional keys to dict
186
- # 3.1 Cache hit/miss/hitpass OVERALL in percent
187
- cache_summary = sum([to_netdata.get('cache_hit', 0), to_netdata.get('cache_miss', 0),
188
- to_netdata.get('cache_hitpass', 0)])
189
- to_netdata['cache_hit_perc'] = find_percent(to_netdata.get('cache_hit', 0), cache_summary, 10000)
190
- to_netdata['cache_miss_perc'] = find_percent(to_netdata.get('cache_miss', 0), cache_summary, 10000)
191
- to_netdata['cache_hitpass_perc'] = find_percent(to_netdata.get('cache_hitpass', 0), cache_summary, 10000)
192
-
193
- # 3.2 Cache hit/miss/hitpass CURRENT in percent
194
- if self.cache_prev:
195
- cache_summary = sum([to_netdata.get('cache_hit', 0), to_netdata.get('cache_miss', 0),
196
- to_netdata.get('cache_hitpass', 0)]) - sum(self.cache_prev)
197
- to_netdata['cache_hit_cperc'] = find_percent(to_netdata.get('cache_hit', 0)
198
- - self.cache_prev[0], cache_summary, 10000)
199
- to_netdata['cache_miss_cperc'] = find_percent(to_netdata.get('cache_miss', 0)
200
- - self.cache_prev[1], cache_summary, 10000)
201
- to_netdata['cache_hitpass_cperc'] = find_percent(to_netdata.get('cache_hitpass', 0)
202
- - self.cache_prev[2], cache_summary, 10000)
203
- else:
204
- to_netdata['cache_hit_cperc'] = 0
205
- to_netdata['cache_miss_cperc'] = 0
206
- to_netdata['cache_hitpass_cperc'] = 0
207
-
208
- self.cache_prev = [to_netdata.get('cache_hit', 0),
209
- to_netdata.get('cache_miss', 0),
210
- to_netdata.get('cache_hitpass', 0)]
211
-
212
- # 3.3 Problems summary chart
213
- for elem in ['backend_busy', 'backend_unhealthy', 'esi_errors',
214
- 'esi_warnings', 'losthdr', 'sess_drop', 'sc_pipe_overflow',
215
- 'sess_fail', 'sess_pipe_overflow', 'threads_destroyed',
216
- 'threads_failed', 'threads_limited', 'thread_queue_len']:
217
- if to_netdata.get(elem) is not None:
218
- to_netdata[''.join([elem, '_b'])] = to_netdata.get(elem)
219
-
220
- return to_netdata
221
-
222
- def create_charts(self):
223
- if self.backend_list:
224
- for backend in self.backend_list:
225
- self.order.insert(0, ''.join([backend[0], '_resp_stats']))
226
- self.definitions.update({''.join([backend[0], '_resp_stats']): {
227
- 'options': [None,
228
- '%s response statistics' % backend[0].capitalize(),
229
- "kilobit/s",
230
- 'Backend response',
231
- 'varnish.backend',
232
- 'area'],
233
- 'lines': [[''.join([backend[0], '_beresp_hdrbytes']),
234
- 'header', 'incremental', 8, 1000],
235
- [''.join([backend[0], '_beresp_bodybytes']),
236
- 'body', 'incremental', -8, 1000]]}})
237
-
238
-
239
-def find_percent(value1, value2, multiply):
240
- # If value2 is 0 return 0
241
- if not value2:
242
- return 0
243
- return round(float(value1) / float(value2) * multiply)
210
+ if self.parser.re_backend:
211
+ backend_stats = self.parser.backend_stats(raw)
212
+ data.update(dict(('_'.join([name, param]), value) for name, param, value in backend_stats))
213
+
214
+ data.update(dict((param, value) for _, param, value in server_stats))
215
+
216
+ data['memory_allocated'] = int(data['s0.g_bytes'])
217
+ data['memory_free'] = int(data['s0.g_space']) - data['memory_allocated']
218
+
219
+ return data
220
+
221
+ def create_backends_charts(self, backends):
222
+ for backend in backends:
223
+ chart_name = ''.join([backend, '_response_statistics'])
224
+ title = 'Backend "{0}" Response Statistics'.format(backend.capitalize())
225
+ hdr_bytes = ''.join([backend, '_beresp_hdrbytes'])
226
+ body_bytes = ''.join([backend, '_beresp_bodybytes'])
227
+
228
+ chart = {
229
+ chart_name:
230
+ {
231
+ 'options': [None, title, 'kilobits/s', 'backend response statistics',
232
+ 'varnish.backend', 'area'],
233
+ 'lines': [
234
+ [hdr_bytes, 'header', 'incremental', 8, 1000],
235
+ [body_bytes, 'body', 'incremental', -8, 1000]
236
+ ]
237
+ }
238
+ }
239
+
240
+ self.order.insert(0, chart_name)
241
+ self.definitions.update(chart)