@cryptotaxi247 / netdata-1 / commits / 6ebe39df4

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)