litespeed python module added
lgz committed
Jun 4, 2018 at 14:47 UTC
651ae55ff0352e662347906b688ca7a6c602af15
1 file changed
+122
python.d/litespeed.chart.py
new
+122
@@ -0,0 +1,122 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: litespeed netdata python.d module
3
+# Author: Ilya Maschenko (l2isbad)
4
+
5
+import glob
6
+import re
7
+import os
8
+
9
+from bases.FrameworkServices.SimpleService import SimpleService
10
+
11
+# charts order (can be overridden if you want less charts, or different order)
12
+ORDER = ['requests', 'pub_cache_hits', 'priv_cache_hits', 'static_hits']
13
+
14
+CHARTS = {
15
+ 'requests': {
16
+ 'options': [None, 'Requests', 'req/sec', 'requests', 'litespeed.requests', 'line'],
17
+ 'lines': [
18
+ ["requests", None, "incremental", 1, 100]
19
+ ]},
20
+ 'pub_cache_hits': {
21
+ 'options': [None, 'Public Cache Hits', 'hits/sec', 'cache', 'litespeed.pub_cache', 'line'],
22
+ 'lines': [
23
+ ["pub_cache_hits", "hits", "incremental", 1, 100]
24
+ ]},
25
+ 'priv_cache_hits': {
26
+ 'options': [None, 'Private Cache Hits', 'hits/sec', 'cache', 'litespeed.priv_cache', 'line'],
27
+ 'lines': [
28
+ ["priv_cache_hits", "hits", "incremental", 1, 100]
29
+ ]},
30
+ 'static_hits': {
31
+ 'options': [None, 'Static Hits', 'hits/sec', 'static', 'litespeed.static', 'line'],
32
+ 'lines': [
33
+ ["static_hits", "hits", "incremental", 1, 100]
34
+ ]},
35
+}
36
+
37
+KEYS = [
38
+ ('TOT_REQS', 'requests'),
39
+ ('TOTAL_PUB_CACHE_HITS', 'pub_cache_hits'),
40
+ ('TOTAL_PRIVATE_CACHE_HITS', 'priv_cache_hits'),
41
+ ('TOTAL_STATIC_HITS', 'static_hits'),
42
+]
43
+
44
+RE = re.compile(r'([A-Z_]+): ([0-9.]+)')
45
+
46
+
47
+class DataFile:
48
+ def __init__(self, abs_path):
49
+ self.path = abs_path
50
+ self.mtime = os.stat(abs_path).st_mtime
51
+
52
+ def is_changed(self):
53
+ o = self.mtime
54
+ n = os.stat(self.path).st_mtime
55
+ self.mtime = n
56
+ return o != n
57
+
58
+
59
+class Service(SimpleService):
60
+ def __init__(self, configuration=None, name=None):
61
+ SimpleService.__init__(self, configuration=configuration, name=name)
62
+ self.order = ORDER
63
+ self.definitions = CHARTS
64
+ self.path = self.configuration.get('path')
65
+ self.files = list()
66
+ self.data = {
67
+ "requests": 0,
68
+ "pub_cache_hits": 0,
69
+ "priv_cache_hits": 0,
70
+ "static_hits": 0,
71
+ }
72
+
73
+ def check(self):
74
+ if not self.path:
75
+ self.error('"path" not specified')
76
+ return False
77
+
78
+ fs = glob.glob(os.path.join(self.path, ".rtreport*"))
79
+
80
+ if not fs:
81
+ self.error('"{0}" has no "rtreport" files or dir is not readable'.format(self.path))
82
+ return None
83
+
84
+ for f in fs:
85
+ if not is_readable_file(f):
86
+ self.error("{0} is not readable".format(f))
87
+ continue
88
+ self.files.append(DataFile(f))
89
+
90
+ return bool(self.files)
91
+
92
+ def get_data(self):
93
+ """
94
+ Format data received from http request
95
+ :return: dict
96
+ """
97
+ for f in self.files:
98
+ try:
99
+ if not f.is_changed():
100
+ continue
101
+ with open(f.path) as b:
102
+ lines = b.readlines()
103
+ except (OSError, IOError) as err:
104
+ self.error(err)
105
+ return None
106
+ else:
107
+ self.parse_file(lines)
108
+
109
+ return self.data
110
+
111
+ def parse_file(self, lines):
112
+ for line in lines:
113
+ if not line.startswith("REQ_RATE []"):
114
+ continue
115
+ m = dict(RE.findall(line))
116
+ for k, d in KEYS:
117
+ self.data[d] = float(m[k]) * 100 if k in m else 0
118
+ break
119
+
120
+
121
+def is_readable_file(v):
122
+ return os.path.isfile(v) and os.access(v, os.R_OK)