@cryptotaxi247 / netdata-1 / commits / cca1e3775

initial version of varnish plugin added

Ilya committed Jan 14, 2017 at 18:27 UTC cca1e3775cf7d9e80214267597a645eb12258f0e
1 file changed +277
python.d/varnish.chart.py new
+277
@@ -0,0 +1,277 @@
1 +# -*- coding: utf-8 -*-
2 +# Description: varnish netdata python.d module
3 +# Author: l2isbad
4 +
5 +from base import SimpleService
6 +from re import compile
7 +from os import access as is_executable, X_OK
8 +from subprocess import Popen, PIPE
9 +
10 +
11 +# default module values (can be overridden per job in `config`)
12 +# update_every = 2
13 +priority = 60000
14 +retries = 60
15 +
16 +ORDER = ['request_rate', 'hit_rate', 'transfer_rates', 'session', 'backend_traffic', 'bad', 'uptime']
17 +EXTRA_ORDER = ['request_rate', 'hit_rate', 'backend_traffic', 'objects', 'transfer_rates', 'threads', 'memory_usage',
18 + 'objects_per_objhead', 'losthdr', 'hcb', 'esi', 'session', 'session_herd', 'shm_writes',
19 + 'shm', 'allocations', 'vcl', 'bans', 'bans_lurker', 'expunge', 'lru', 'bad', 'gzip', 'uptime']
20 +
21 +CHARTS = {'allocations':
22 + {'lines': [['sm_nreq', None, 'incremental', 1, 1],
23 + ['sma_nreq', None, 'incremental', 1, 1],
24 + ['sms_nreq', None, 'incremental', 1, 1]],
25 + 'options': [None, 'Memory allocation requests', 'units', 'allocations', 'varnish.alloc','line']},
26 + 'backend_traffic':
27 + {'lines': [['backend_conn_bt', 'conn', 'incremental', 1, 1],
28 + ['backend_unhealthy', 'unhealthy', 'incremental', 1, 1],
29 + ['backend_busy', 'busy', 'incremental', 1, 1],
30 + ['backend_fail', 'fail', 'incremental', 1, 1],
31 + ['backend_reuse', 'reuse', 'incremental', 1, 1],
32 + ['backend_recycle', 'resycle', 'incremental', 1, 1],
33 + ['backend_toolate', 'toolate', 'incremental', 1, 1],
34 + ['backend_retry', 'retry', 'incremental', 1, 1],
35 + ['backend_req', 'req', 'incremental', 1, 1]],
36 + 'options': [None, 'Backend traffic', 'units', 'backend_traffic', 'varnish.backend_traf', 'line']},
37 + 'bad':
38 + {'lines': [['sess_drop_b', None, 'incremental', 1, 1],
39 + ['backend_unhealthy_b', None, 'incremental', 1, 1],
40 + ['fetch_failed', None, 'incremental', 1, 1],
41 + ['backend_busy_b', None, 'incremental', 1, 1],
42 + ['threads_failed_b', None, 'incremental', 1, 1],
43 + ['threads_limited_b', None, 'incremental', 1, 1],
44 + ['threads_destroyed_b', None, 'incremental', 1, 1],
45 + ['thread_queue_len', None, 'absolute', 1, 1],
46 + ['losthdr_b', None, 'incremental', 1, 1],
47 + ['esi_errors_b', None, 'incremental', 1, 1],
48 + ['esi_warnings_b', None, 'incremental', 1, 1],
49 + ['sess_fail_b', None, 'incremental', 1, 1],
50 + ['sess_pipe_overflow_b', None, 'incremental', 1, 1]],
51 + 'options': [None, 'Misbehavior', 'units', 'bad', 'varnish.bad', 'line']},
52 + 'bans':
53 + {'lines': [['bans', None, 'absolute', 1, 1],
54 + ['bans_added', 'added', 'incremental', 1, 1],
55 + ['bans_deleted', 'deleted', 'incremental', 1, 1],
56 + ['bans_completed', 'completed', 'absolute', 1, 1],
57 + ['bans_obj', 'obj', 'absolute', 1, 1],
58 + ['bans_req', 'req', 'absolute', 1, 1],
59 + ['bans_tested', 'tested', 'incremental', 1, 1],
60 + ['bans_obj_killed', 'obj_killed', 'incremental', 1, 1],
61 + ['bans_tests_tested', 'tests_tested', 'incremental', 1, 1],
62 + ['bans_dups', 'dups', 'absolute', 1, 1],
63 + ['bans_persisted_bytes', 'pers_bytes', 'absolute', 1, 1],
64 + ['bans_persisted_fragmentation', 'pers_fragmentation', 'absolute', 1, 1]],
65 + 'options': [None, 'Bans', 'units', 'bans', 'varnish.bans', 'line']},
66 + 'bans_lurker':
67 + {'lines': [['bans_lurker_tested', 'tested', 'incremental', 1, 1],
68 + ['bans_lurker_tests_tested', 'tests_tested', 'incremental', 1, 1],
69 + ['bans_lurker_obj_killed', 'obj_killed', 'incremental', 1, 1],
70 + ['bans_lurker_contention', 'contention', 'incremental', 1, 1]],
71 + 'options': [None, 'Ban Lurker', 'units', 'bans_lurker', 'varnish.bans_lurker', 'line']},
72 + 'esi':
73 + {'lines': [['esi_parse', None, 'incremental', 1, 1],
74 + ['esi_errors', None, 'incremental', 1, 1],
75 + ['esi_warnings', None, 'incremental', 1, 1]],
76 + 'options': [None, 'ESI', 'units', 'esi', 'varnish.esi', 'line']},
77 + 'expunge':
78 + {'lines': [['n_expired', None, 'incremental', 1, 1],
79 + ['n_lru_nuked_e', None, 'incremental', 1, 1]],
80 + 'options': [None, 'Object expunging', 'units', 'expunge', 'varnish.expunge', 'line']},
81 + 'gzip':
82 + {'lines': [['n_gzip', None, 'incremental', 1, 1],
83 + ['n_gunzip', None, 'incremental', 1, 1]],
84 + 'options': [None, 'GZIP activity', 'units', 'gzip', 'varnish.gzip', 'line']},
85 + 'hcb':
86 + {'lines': [['hcb_nolock', 'nolock', 'incremental', 1, 1],
87 + ['hcb_lock', 'lock', 'incremental', 1, 1],
88 + ['hcb_insert', 'insert', 'incremental', 1, 1]],
89 + 'options': [None, 'Critbit data', 'units', 'hcb', 'varnish.hcb', 'line']},
90 + 'hit_rate':
91 + {'lines': [['cache_hit_perc', 'hit', 'absolute', 1, 100],
92 + ['cache_miss_perc', 'miss', 'absolute', 1, 100],
93 + ['cache_hitpass_perc', 'hitpass', 'absolute', 1, 100]],
94 + 'options': [None, 'Hit rates','percent', 'hit_rate', 'varnish.hit_rate', 'line']},
95 + 'losthdr':
96 + {'lines': [['losthdr', None, 'incremental', 1, 1]],
97 + 'options': [None, 'HTTP Header overflows', 'units', 'losthdr', 'varnish.losthdr', 'line']},
98 + 'lru':
99 + {'lines': [['n_lru_nuked', 'nuked', 'incremental', 1, 1],
100 + ['n_lru_moved', 'moved', 'incremental', 1, 1]],
101 + 'options': [None, 'LRU activity', 'units', 'lru', 'varnish.lru', 'line']},
102 + 'memory_usage':
103 + {'lines': [['sms_balloc', None, 'absolute', 1, 1],
104 + ['sms_nbytes', None, 'absolute', 1, 1]],
105 + 'options': [None, 'Memory usage', 'units', 'memory_usage', 'varnish.memory_usage', 'line']},
106 + 'objects':
107 + {'lines': [['n_object', 'object', 'absolute', 1, 1],
108 + ['n_objectcore', 'objectcore', 'absolute', 1, 1],
109 + ['n_vampireobject', 'vampireobject, ''absolute', 1, 1],
110 + ['n_objecthead', 'objecthead', 'absolute', 1, 1]],
111 + 'options': [None, 'Number of objects', 'units', 'objects', 'varnish.objects', 'line']},
112 + 'objects_per_objhead':
113 + {'lines': [['obj_per_objhead', 'per_objhead', 'absolute', 1, 100]],
114 + 'options': [None, 'Objects per objecthead', 'units', 'objects_per_objhead', 'varnish.objects_per_objhead', 'line']},
115 + 'request_rate':
116 + {'lines': [['sess_conn_rr', None, 'incremental', 1, 1],
117 + ['client_req', None, 'incremental', 1, 1],
118 + ['cache_hit', None, 'incremental', 1, 1],
119 + ['cache_hitpass', None, 'incremental', 1, 1],
120 + ['cache_miss', None, 'incremental', 1, 1],
121 + ['backend_conn', None, 'incremental', 1, 1],
122 + ['backend_unhealthy', None, 'incremental', 1, 1],
123 + ['s_pipe', None, 'incremental', 1, 1],
124 + ['s_pass', None, 'incremental', 1, 1]],
125 + 'options': [None, 'Request rates', 'units', 'request_rate', 'varnish.request_rate', 'line']},
126 + 'session':
127 + {'lines': [['sess_conn', 'conn', 'incremental', 1, 1],
128 + ['sess_drop', 'drop', 'incremental', 1, 1],
129 + ['sess_fail', 'fail', 'incremental', 1, 1],
130 + ['sess_pipe_overflow', 'pipe_overflow', 'incremental', 1, 1],
131 + ['sess_queued', 'queued', 'incremental', 1, 1],
132 + ['sess_dropped', 'dropped', 'incremental', 1, 1],
133 + ['sess_closed', 'closed', 'incremental', 1, 1],
134 + ['sess_pipeline', 'pipeline', 'incremental', 1, 1],
135 + ['sess_readahead' , 'readhead', 'incremental', 1, 1]],
136 +
137 + 'options': [None, 'Sessions', 'units', 'session', 'varnish.session', 'line']},
138 + 'session_herd':
139 + {'lines': [['sess_herd', None, 'incremental', 1, 1]],
140 + 'options': [None, 'Session herd', 'units', 'session_herd', 'varnish.session_herd', 'line']},
141 + 'shm':
142 + {'lines': [['shm_flushes', 'flushes', 'incremental', 1, 1],
143 + ['shm_cont', 'cont', 'incremental', 1, 1],
144 + ['shm_cycles', 'cycles', 'incremental', 1, 1]],
145 + 'options': [None, 'SHM writes and records', 'units', 'shm', 'varnish.shm', 'line']},
146 + 'shm_writes':
147 + {'lines': [['shm_records', 'records', 'incremental', 1, 1],
148 + ['shm_writes', 'writes', 'incremental', 1, 1]],
149 + 'options': [None, 'SHM writes and records', 'units', 'shm_writes', 'varnish.shm_writes', 'line']},
150 + 'threads':
151 + {'lines': [['threads', None, 'absolute', 1, 1],
152 + ['threads_created', 'created', 'incremental', 1, 1],
153 + ['threads_failed', 'failed', 'incremental', 1, 1],
154 + ['threads_limited', 'limited', 'incremental', 1, 1],
155 + ['threads_destroyed', 'destroyed', 'incremental', 1, 1]],
156 + 'options': [None, 'Thread status', 'units', 'threads', 'varnish.threads', 'line']},
157 + 'transfer_rates':
158 + {'lines': [['s_resp_hdrbytes', 'resp_hdrbytes', 'incremental', 8, 1],
159 + ['s_resp_bodybytes', 'resp_bodybytes', 'incremental', 8, 1]],
160 + 'options': [None, 'Transfer rates', 'bits/s', 'transfer_rates', 'varnish.transfer_rates', 'line']},
161 + 'uptime':
162 + {'lines': [['uptime', None, 'absolute', 1, 1]],
163 + 'options': [None, 'Varnish uptime', 'seconds', 'uptime', 'varnish.uptime', 'line']},
164 + 'vcl':
165 + {'lines': [['n_backend', None, 'absolute', 1, 1],
166 + ['n_vcl', None, 'incremental', 1, 1],
167 + ['n_vcl_avail', None, 'incremental', 1, 1],
168 + ['n_vcl_discard', None, 'incremental', 1, 1]],
169 + 'options': [None, 'VCL', 'units', 'vcl', 'varnish.vcl', 'line']}}
170 +
171 +DIRECTORIES = ['/bin/', '/usr/bin/', '/sbin/', '/usr/sbin/']
172 +
173 +
174 +class Service(SimpleService):
175 + def __init__(self, configuration=None, name=None):
176 + SimpleService.__init__(self, configuration=configuration, name=name)
177 + try:
178 + self.varnish = [''.join([directory, 'varnishstat']) for directory in DIRECTORIES
179 + if is_executable(''.join([directory, 'varnishstat']), X_OK)][0]
180 + except IndexError:
181 + self.varnish = False
182 + self.regex = compile(r'([A-Z]+\.)([\d\w_.]+)\s+([0-9]+)')
183 + self.extra_charts = self.configuration.get('extra_charts', [])
184 +
185 + def check(self):
186 + # Cant start without varnishstat command
187 + if not self.varnish:
188 + self.error('varnishstat command was not found in %s or not executable by netdata' % DIRECTORIES)
189 + return False
190 +
191 + # If command is present and we can execute it we need to make sure..
192 + # 1. STDOUT is not empty
193 + reply = self._get_raw_data()
194 + if not reply:
195 + self.error('No output from varnishstat (not enough privileges?)')
196 + return False
197 +
198 + # 2. Output is parsable (list is not empty after regex findall)
199 + is_parsable = self.regex.findall(reply)
200 + if not is_parsable:
201 + self.error('Cant parse output (only varnish version 4+ supported)')
202 + return False
203 +
204 + # We are about to start!
205 + self.create_charts()
206 +
207 + self.info('Active charts: %s' % self.order)
208 + self.info('Plugin was started succesfully')
209 + return True
210 +
211 +
212 + def _get_raw_data(self):
213 + try:
214 + reply = Popen([self.varnish, '-1'], stdout=PIPE, stderr=PIPE, shell=False)
215 + except OSError:
216 + return None
217 +
218 + raw_data = reply.stdout.read()
219 +
220 + if not raw_data:
221 + return None
222 +
223 + return raw_data
224 +
225 + def _get_data(self):
226 + """
227 + Format data received from shell command
228 + :return: dict
229 + """
230 + raw_data = self._get_raw_data()
231 + data = self.regex.findall(raw_data)
232 +
233 + if not data:
234 + return None
235 +
236 + # ALL data from 'varnishstat -1'. t - type(MAIN, MEMPOOL etc)
237 + to_netdata = {k: int(v) for t, k, v in data}
238 +
239 + # ADD additional keys to dict
240 + cache_summary = sum([to_netdata.get('cache_hit', 0), to_netdata.get('cache_miss', 0), to_netdata.get('cache_hitpass', 0)])
241 + to_netdata['cache_hit_perc'] = find_percent(to_netdata.get('cache_hit', 0), cache_summary, 10000)
242 + to_netdata['cache_miss_perc'] = find_percent(to_netdata.get('cache_miss', 0), cache_summary, 10000)
243 + to_netdata['cache_hitpass_perc'] = find_percent(to_netdata.get('cache_hitpass', 0), cache_summary, 10000)
244 + to_netdata['obj_per_objhead'] = find_percent(to_netdata.get('n_object', 0), to_netdata.get('n_objecthead', 0), 100)
245 + to_netdata['backend_conn_bt'] = to_netdata.get('backend_conn', 0)
246 + to_netdata['sess_conn_rr'] = to_netdata.get('sess_conn', 0)
247 + to_netdata['n_lru_nuked_e'] = to_netdata.get('n_lru_nuked', 0)
248 +
249 + for elem in ['backend_busy', 'backend_unhealthy', 'esi_errors', 'esi_warnings', 'losthdr', 'sess_drop', 'sess_fail', 'sess_pipe_overflow',
250 + 'threads_destroyed', 'threads_failed', 'threads_limited']:
251 + to_netdata[''.join([elem, '_b'])] = to_netdata.get(elem, 0)
252 +
253 + return to_netdata
254 +
255 + def create_charts(self):
256 + if self.configuration.get('all_charts'):
257 + self.order = EXTRA_CHARTS
258 + else:
259 + try:
260 + extra_charts = list(filter(lambda chart: chart in EXTRA_ORDER, self.extra_charts.split()))
261 + except Exception:
262 + self.error('Extra charts disabled.')
263 + extra_charts = []
264 +
265 + self.order = ORDER[:]
266 + self.order.extend(extra_charts)
267 + self.order = self.order
268 +
269 + self.definitions = {k: v for k, v in CHARTS.items() if k in self.order}
270 +
271 +
272 +def find_percent(value1, value2, multiply):
273 + # If value 2 is 0 return 0
274 + if not value2:
275 + return 0
276 + else:
277 + return round(float(value1) / float(value2) * multiply)