@cryptotaxi247 / netdata-1 / commits / 54149c562

monit: obsolete service in runtime (#6340)

* obsolete service

Ilya Mashchenko committed Jun 29, 2019 at 21:36 UTC 54149c562c6b11459e5c8cb42a96a01d22d87a64
1 file changed +241 -75
collectors/python.d.plugin/monit/monit.chart.py
+241 -75
@@ -4,23 +4,49 @@
4 # SPDX-License-Identifier: GPL-3.0-or-later
5
6 import xml.etree.ElementTree as ET
7 +
8 +from collections import namedtuple
9 +
10 from bases.FrameworkServices.UrlService import UrlService
11
12
10 -# see enum State_Type from monit.h (https://bitbucket.org/tildeslash/monit/src/master/src/monit.h)
11 -MONIT_SERVICE_NAMES = [
12 - 'Filesystem',
13 - 'Directory',
14 - 'File',
15 - 'Process',
16 - 'Host',
17 - 'System',
18 - 'Fifo',
19 - 'Program',
20 - 'Net',
21 -]
13 +MonitType = namedtuple('MonitType', ('index', 'name'))
14 +
15 +# see enum Service_Type from monit.h (https://bitbucket.org/tildeslash/monit/src/master/src/monit.h)
16 +# typedef enum {
17 +# Service_Filesystem = 0,
18 +# Service_Directory,
19 +# Service_File,
20 +# Service_Process,
21 +# Service_Host,
22 +# Service_System,
23 +# Service_Fifo,
24 +# Service_Program,
25 +# Service_Net,
26 +# Service_Last = Service_Net
27 +# } __attribute__((__packed__)) Service_Type;
28
23 -DEFAULT_SERVICES_IDS = [0, 1, 2, 3, 4, 6, 7, 8]
29 +TYPE_FILESYSTEM = MonitType(0, 'filesystem')
30 +TYPE_DIRECTORY = MonitType(1, 'directory')
31 +TYPE_FILE = MonitType(2, 'file')
32 +TYPE_PROCESS = MonitType(3, 'process')
33 +TYPE_HOST = MonitType(4, 'host')
34 +TYPE_SYSTEM = MonitType(5, 'system')
35 +TYPE_FIFO = MonitType(6, 'fifo')
36 +TYPE_PROGRAM = MonitType(7, 'program')
37 +TYPE_NET = MonitType(8, 'net')
38 +
39 +TYPES = (
40 + TYPE_FILESYSTEM,
41 + TYPE_DIRECTORY,
42 + TYPE_FILE,
43 + TYPE_PROCESS,
44 + TYPE_HOST,
45 + TYPE_SYSTEM,
46 + TYPE_FIFO,
47 + TYPE_PROGRAM,
48 + TYPE_NET,
49 +)
50
51 # charts order (can be overridden if you want less charts, or different order)
52 ORDER = [
@@ -38,6 +64,7 @@ ORDER = [
64 'program',
65 'net'
66 ]
67 +
68 CHARTS = {
69 'filesystem': {
70 'options': ['filesystems', 'Filesystems', 'filesystems', 'filesystem', 'monit.filesystems', 'line'],
@@ -83,7 +110,7 @@ CHARTS = {
110 'lines': []
111 },
112 'host_latency': {
86 - 'options': ['hosts latency', 'Hosts latency', 'milliseconds/s', 'network', 'monit.host_latency', 'line'],
113 + 'options': ['hosts latency', 'Hosts latency', 'milliseconds', 'network', 'monit.host_latency', 'line'],
114 'lines': []
115 },
116 'net': {
@@ -94,85 +121,224 @@ CHARTS = {
121 }
122
123
124 +class BaseMonitService(object):
125 + def __init__(self, typ, name, status, monitor):
126 + self.type = typ
127 + self.name = name
128 + self.status = status
129 + self.monitor = monitor
130 +
131 + def __repr__(self):
132 + return 'MonitService({0}:{1})'.format(self.type.name, self.name)
133 +
134 + def __eq__(self, other):
135 + if not isinstance(other, BaseMonitService):
136 + return False
137 + return self.type == other.type and self.name == other.name
138 +
139 + def __ne__(self, other):
140 + return not self == other
141 +
142 + def __hash__(self):
143 + return hash(repr(self))
144 +
145 + def is_running(self):
146 + return self.status == '0' and self.monitor == '1'
147 +
148 + def key(self):
149 + return '{0}_{1}'.format(self.type.name, self.name)
150 +
151 + def data(self):
152 + return {self.key(): int(self.is_running())}
153 +
154 +
155 +class ProcessMonitService(BaseMonitService):
156 + def __init__(self, typ, name, status, monitor):
157 + super(ProcessMonitService, self).__init__(typ, name, status, monitor)
158 + self.uptime = None
159 + self.threads = None
160 + self.children = None
161 +
162 + def uptime_key(self):
163 + return 'process_uptime_{0}'.format(self.name)
164 +
165 + def threads_key(self):
166 + return 'process_threads_{0}'.format(self.name)
167 +
168 + def children_key(self):
169 + return 'process_children_{0}'.format(self.name)
170 +
171 + def data(self):
172 + base_data = super(ProcessMonitService, self).data()
173 + # skipping bugged metrics with negative uptime (monit before v5.16)
174 + uptime = self.uptime if self.uptime and int(self.uptime) >= 0 else None
175 + data = {
176 + self.uptime_key(): uptime,
177 + self.threads_key(): self.threads,
178 + self.children_key(): self.children,
179 + }
180 + data.update(base_data)
181 +
182 + return data
183 +
184 +
185 +class HostMonitService(BaseMonitService):
186 + def __init__(self, typ, name, status, monitor):
187 + super(HostMonitService, self).__init__(typ, name, status, monitor)
188 + self.latency = None
189 +
190 + def latency_key(self):
191 + return 'host_latency_{0}'.format(self.name)
192 +
193 + def data(self):
194 + base_data = super(HostMonitService, self).data()
195 + latency = float(self.latency) * 1000000 if self.latency else None
196 + data = {self.latency_key(): latency}
197 + data.update(base_data)
198 +
199 + return data
200 +
201 +
202 class Service(UrlService):
203 def __init__(self, configuration=None, name=None):
204 UrlService.__init__(self, configuration=configuration, name=name)
205 self.order = ORDER
206 self.definitions = CHARTS
102 - base_url = self.configuration.get('url', 'http://localhost:2812')
207 + base_url = self.configuration.get('url', "http://localhost:2812")
208 self.url = '{0}/_status?format=xml&level=full'.format(base_url)
209 + self.active_services = list()
210
105 - def parse(self, data):
211 + def parse(self, raw):
212 try:
107 - xml = ET.fromstring(data)
213 + root = ET.fromstring(raw)
214 except ET.ParseError:
109 - self.error("URL {0} didn't return a vaild XML page. Please check your settings.".format(self.url))
215 + self.error("URL {0} didn't return a valid XML page. Please check your settings.".format(self.url))
216 + return None
217 + return root
218 +
219 + def _get_data(self):
220 + raw = self._get_raw_data()
221 + if not raw:
222 return None
111 - return xml
223
113 - def check(self):
114 - self._manager = self._build_manager()
224 + root = self.parse(raw)
225 + if root is None:
226 + return None
227
116 - raw_data = self._get_raw_data()
117 - if not raw_data:
228 + services = self.get_services(root)
229 + if not services:
230 return None
231
120 - return bool(self.parse(raw_data))
232 + if len(self.charts) > 0:
233 + self.update_charts(services)
234
122 - def _get_data(self):
123 - raw_data = self._get_raw_data()
235 + data = dict()
236
125 - if not raw_data:
126 - return None
237 + for svc in services:
238 + data.update(svc.data())
239
128 - xml = self.parse(raw_data)
129 - if not xml:
130 - return None
240 + return data
241
132 - data = {}
133 - for service_id in DEFAULT_SERVICES_IDS:
134 - service_category = MONIT_SERVICE_NAMES[service_id].lower()
242 + def get_services(self, root):
243 + services = list()
244
136 - if service_category == 'system':
137 - self.debug("Skipping service from 'System' category, because it's useless in graphs")
245 + for typ in TYPES:
246 + if typ == TYPE_SYSTEM:
247 + self.debug("skipping service from '{0}' category, it's useless in graphs".format(TYPE_SYSTEM.name))
248 continue
249
140 - xpath_query = "./service[@type='{0}']".format(service_id)
141 - self.debug('Searching for {0} as {1}'.format(service_category, xpath_query))
142 - for service_node in xml.findall(xpath_query):
143 -
144 - service_name = service_node.find('name').text
145 - service_status = service_node.find('status').text
146 - service_monitoring = service_node.find('monitor').text
147 - self.debug('=> found {0} with type={1}, status={2}, monitoring={3}'.format(service_name,
148 - service_id, service_status, service_monitoring))
149 -
150 - dimension_key = service_category + '_' + service_name
151 - if dimension_key not in self.charts[service_category]:
152 - self.charts[service_category].add_dimension([dimension_key, service_name, 'absolute'])
153 - data[dimension_key] = 1 if service_status == '0' and service_monitoring == '1' else 0
154 -
155 - if service_category == 'process':
156 - for subnode in ('uptime', 'threads', 'children'):
157 - subnode_value = service_node.find(subnode)
158 - if subnode_value is None:
159 - continue
160 - if subnode == 'uptime' and int(subnode_value.text) < 0:
161 - self.debug('Skipping bugged metrics with negative uptime (monit before v5.16')
162 - continue
163 - dimension_key = 'process_{0}_{1}'.format(subnode, service_name)
164 - if dimension_key not in self.charts['process_' + subnode]:
165 - self.charts['process_' + subnode].add_dimension([dimension_key, service_name, 'absolute'])
166 - data[dimension_key] = int(subnode_value.text)
167 -
168 - if service_category == 'host':
169 - subnode_value = service_node.find('./icmp/responsetime')
170 - if subnode_value is None:
171 - continue
172 - dimension_key = 'host_latency_{0}'.format(service_name)
173 - if dimension_key not in self.charts['host_latency']:
174 - self.charts['host_latency'].add_dimension([dimension_key, service_name,
175 - 'absolute', 1000, 1000000])
176 - data[dimension_key] = float(subnode_value.text) * 1000000
177 -
178 - return data or None
250 + xpath_query = "./service[@type='{0}']".format(typ.index)
251 + self.debug('Searching for {0} as {1}'.format(typ.name, xpath_query))
252 +
253 + for svc_root in root.findall(xpath_query):
254 + svc = create_service(svc_root, typ)
255 + self.debug('=> found {0} with type={1}, status={2}, monitoring={3}'.format(
256 + svc.name, svc.type.name, svc.status, svc.monitor))
257 +
258 + services.append(svc)
259 +
260 + return services
261 +
262 + def update_charts(self, services):
263 + remove = [svc for svc in self.active_services if svc not in services]
264 + add = [svc for svc in services if svc not in self.active_services]
265 +
266 + self.remove_services_from_charts(remove)
267 + self.add_services_to_charts(add)
268 +
269 + self.active_services = services
270 +
271 + def add_services_to_charts(self, services):
272 + for svc in services:
273 + if svc.type == TYPE_HOST:
274 + self.charts['host_latency'].add_dimension([svc.latency_key(), svc.name, 'absolute', 1000, 1000000])
275 + if svc.type == TYPE_PROCESS:
276 + self.charts['process_uptime'].add_dimension([svc.uptime_key(), svc.name])
277 + self.charts['process_threads'].add_dimension([svc.threads_key(), svc.name])
278 + self.charts['process_children'].add_dimension([svc.children_key(), svc.name])
279 + self.charts[svc.type.name].add_dimension([svc.key(), svc.name])
280 +
281 + def remove_services_from_charts(self, services):
282 + for svc in services:
283 + if svc.type == TYPE_HOST:
284 + self.charts['host_latency'].del_dimension(svc.latency_key(), False)
285 + if svc.type == TYPE_PROCESS:
286 + self.charts['process_uptime'].del_dimension(svc.uptime_key(), False)
287 + self.charts['process_threads'].del_dimension(svc.threads_key(), False)
288 + self.charts['process_children'].del_dimension(svc.children_key(), False)
289 + self.charts[svc.type.name].del_dimension(svc.key(), False)
290 +
291 +
292 +def create_service(root, typ):
293 + if typ == TYPE_HOST:
294 + return create_host_service(root)
295 + elif typ == TYPE_PROCESS:
296 + return create_process_service(root)
297 + return create_base_service(root, typ)
298 +
299 +
300 +def create_host_service(root):
301 + svc = HostMonitService(
302 + TYPE_HOST,
303 + root.find('name').text,
304 + root.find('status').text,
305 + root.find('monitor').text,
306 + )
307 +
308 + latency = root.find('./icmp/responsetime')
309 + if latency is not None:
310 + svc.latency = latency.text
311 +
312 + return svc
313 +
314 +
315 +def create_process_service(root):
316 + svc = ProcessMonitService(
317 + TYPE_PROCESS,
318 + root.find('name').text,
319 + root.find('status').text,
320 + root.find('monitor').text,
321 + )
322 +
323 + uptime = root.find('uptime')
324 + if uptime is not None:
325 + svc.uptime = uptime.text
326 +
327 + threads = root.find('threads')
328 + if threads is not None:
329 + svc.threads = threads.text
330 +
331 + children = root.find('children')
332 + if children is not None:
333 + svc.children = children.text
334 +
335 + return svc
336 +
337 +
338 +def create_base_service(root, typ):
339 + return BaseMonitService(
340 + typ,
341 + root.find('name').text,
342 + root.find('status').text,
343 + root.find('monitor').text,
344 + )