monit.chart.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. # -*- coding: utf-8 -*-
  2. # Description: monit netdata python.d module
  3. # Author: Evgeniy K. (n0guest)
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. import xml.etree.ElementTree as ET
  6. from collections import namedtuple
  7. from bases.FrameworkServices.UrlService import UrlService
  8. MonitType = namedtuple('MonitType', ('index', 'name'))
  9. # see enum Service_Type from monit.h (https://bitbucket.org/tildeslash/monit/src/master/src/monit.h)
  10. # typedef enum {
  11. # Service_Filesystem = 0,
  12. # Service_Directory,
  13. # Service_File,
  14. # Service_Process,
  15. # Service_Host,
  16. # Service_System,
  17. # Service_Fifo,
  18. # Service_Program,
  19. # Service_Net,
  20. # Service_Last = Service_Net
  21. # } __attribute__((__packed__)) Service_Type;
  22. TYPE_FILESYSTEM = MonitType(0, 'filesystem')
  23. TYPE_DIRECTORY = MonitType(1, 'directory')
  24. TYPE_FILE = MonitType(2, 'file')
  25. TYPE_PROCESS = MonitType(3, 'process')
  26. TYPE_HOST = MonitType(4, 'host')
  27. TYPE_SYSTEM = MonitType(5, 'system')
  28. TYPE_FIFO = MonitType(6, 'fifo')
  29. TYPE_PROGRAM = MonitType(7, 'program')
  30. TYPE_NET = MonitType(8, 'net')
  31. TYPES = (
  32. TYPE_FILESYSTEM,
  33. TYPE_DIRECTORY,
  34. TYPE_FILE,
  35. TYPE_PROCESS,
  36. TYPE_HOST,
  37. TYPE_SYSTEM,
  38. TYPE_FIFO,
  39. TYPE_PROGRAM,
  40. TYPE_NET,
  41. )
  42. # charts order (can be overridden if you want less charts, or different order)
  43. ORDER = [
  44. 'filesystem',
  45. 'directory',
  46. 'file',
  47. 'process',
  48. 'process_uptime',
  49. 'process_threads',
  50. 'process_children',
  51. 'host',
  52. 'host_latency',
  53. 'system',
  54. 'fifo',
  55. 'program',
  56. 'net'
  57. ]
  58. CHARTS = {
  59. 'filesystem': {
  60. 'options': ['filesystems', 'Filesystems', 'filesystems', 'filesystem', 'monit.filesystems', 'line'],
  61. 'lines': []
  62. },
  63. 'directory': {
  64. 'options': ['directories', 'Directories', 'directories', 'filesystem', 'monit.directories', 'line'],
  65. 'lines': []
  66. },
  67. 'file': {
  68. 'options': ['files', 'Files', 'files', 'filesystem', 'monit.files', 'line'],
  69. 'lines': []
  70. },
  71. 'fifo': {
  72. 'options': ['fifos', 'Pipes (fifo)', 'pipes', 'filesystem', 'monit.fifos', 'line'],
  73. 'lines': []
  74. },
  75. 'program': {
  76. 'options': ['programs', 'Programs statuses', 'programs', 'applications', 'monit.programs', 'line'],
  77. 'lines': []
  78. },
  79. 'process': {
  80. 'options': ['processes', 'Processes statuses', 'processes', 'applications', 'monit.services', 'line'],
  81. 'lines': []
  82. },
  83. 'process_uptime': {
  84. 'options': ['processes uptime', 'Processes uptime', 'seconds', 'applications',
  85. 'monit.process_uptime', 'line', 'hidden'],
  86. 'lines': []
  87. },
  88. 'process_threads': {
  89. 'options': ['processes threads', 'Processes threads', 'threads', 'applications',
  90. 'monit.process_threads', 'line'],
  91. 'lines': []
  92. },
  93. 'process_children': {
  94. 'options': ['processes childrens', 'Child processes', 'children', 'applications',
  95. 'monit.process_childrens', 'line'],
  96. 'lines': []
  97. },
  98. 'host': {
  99. 'options': ['hosts', 'Hosts', 'hosts', 'network', 'monit.hosts', 'line'],
  100. 'lines': []
  101. },
  102. 'host_latency': {
  103. 'options': ['hosts latency', 'Hosts latency', 'milliseconds', 'network', 'monit.host_latency', 'line'],
  104. 'lines': []
  105. },
  106. 'net': {
  107. 'options': ['interfaces', 'Network interfaces and addresses', 'interfaces', 'network',
  108. 'monit.networks', 'line'],
  109. 'lines': []
  110. },
  111. }
  112. class BaseMonitService(object):
  113. def __init__(self, typ, name, status, monitor):
  114. self.type = typ
  115. self.name = name
  116. self.status = status
  117. self.monitor = monitor
  118. def __repr__(self):
  119. return 'MonitService({0}:{1})'.format(self.type.name, self.name)
  120. def __eq__(self, other):
  121. if not isinstance(other, BaseMonitService):
  122. return False
  123. return self.type == other.type and self.name == other.name
  124. def __ne__(self, other):
  125. return not self == other
  126. def __hash__(self):
  127. return hash(repr(self))
  128. def is_running(self):
  129. return self.status == '0' and self.monitor == '1'
  130. def key(self):
  131. return '{0}_{1}'.format(self.type.name, self.name)
  132. def data(self):
  133. return {self.key(): int(self.is_running())}
  134. class ProcessMonitService(BaseMonitService):
  135. def __init__(self, typ, name, status, monitor):
  136. super(ProcessMonitService, self).__init__(typ, name, status, monitor)
  137. self.uptime = None
  138. self.threads = None
  139. self.children = None
  140. def __eq__(self, other):
  141. return super(ProcessMonitService, self).__eq__(other)
  142. def __ne__(self, other):
  143. return super(ProcessMonitService, self).__ne__(other)
  144. def __hash__(self):
  145. return super(ProcessMonitService, self).__hash__()
  146. def uptime_key(self):
  147. return 'process_uptime_{0}'.format(self.name)
  148. def threads_key(self):
  149. return 'process_threads_{0}'.format(self.name)
  150. def children_key(self):
  151. return 'process_children_{0}'.format(self.name)
  152. def data(self):
  153. base_data = super(ProcessMonitService, self).data()
  154. # skipping bugged metrics with negative uptime (monit before v5.16)
  155. uptime = self.uptime if self.uptime and int(self.uptime) >= 0 else None
  156. data = {
  157. self.uptime_key(): uptime,
  158. self.threads_key(): self.threads,
  159. self.children_key(): self.children,
  160. }
  161. data.update(base_data)
  162. return data
  163. class HostMonitService(BaseMonitService):
  164. def __init__(self, typ, name, status, monitor):
  165. super(HostMonitService, self).__init__(typ, name, status, monitor)
  166. self.latency = None
  167. def __eq__(self, other):
  168. return super(HostMonitService, self).__eq__(other)
  169. def __ne__(self, other):
  170. return super(HostMonitService, self).__ne__(other)
  171. def __hash__(self):
  172. return super(HostMonitService, self).__hash__()
  173. def latency_key(self):
  174. return 'host_latency_{0}'.format(self.name)
  175. def data(self):
  176. base_data = super(HostMonitService, self).data()
  177. latency = float(self.latency) * 1000000 if self.latency else None
  178. data = {self.latency_key(): latency}
  179. data.update(base_data)
  180. return data
  181. class Service(UrlService):
  182. def __init__(self, configuration=None, name=None):
  183. UrlService.__init__(self, configuration=configuration, name=name)
  184. self.order = ORDER
  185. self.definitions = CHARTS
  186. base_url = self.configuration.get('url', "http://localhost:2812")
  187. self.url = '{0}/_status?format=xml&level=full'.format(base_url)
  188. self.active_services = list()
  189. def parse(self, raw):
  190. try:
  191. root = ET.fromstring(raw)
  192. except ET.ParseError:
  193. self.error("URL {0} didn't return a valid XML page. Please check your settings.".format(self.url))
  194. return None
  195. return root
  196. def _get_data(self):
  197. raw = self._get_raw_data()
  198. if not raw:
  199. return None
  200. root = self.parse(raw)
  201. if root is None:
  202. return None
  203. services = self.get_services(root)
  204. if not services:
  205. return None
  206. if len(self.charts) > 0:
  207. self.update_charts(services)
  208. data = dict()
  209. for svc in services:
  210. data.update(svc.data())
  211. return data
  212. def get_services(self, root):
  213. services = list()
  214. for typ in TYPES:
  215. if typ == TYPE_SYSTEM:
  216. self.debug("skipping service from '{0}' category, it's useless in graphs".format(TYPE_SYSTEM.name))
  217. continue
  218. xpath_query = "./service[@type='{0}']".format(typ.index)
  219. self.debug('Searching for {0} as {1}'.format(typ.name, xpath_query))
  220. for svc_root in root.findall(xpath_query):
  221. svc = create_service(svc_root, typ)
  222. self.debug('=> found {0} with type={1}, status={2}, monitoring={3}'.format(
  223. svc.name, svc.type.name, svc.status, svc.monitor))
  224. services.append(svc)
  225. return services
  226. def update_charts(self, services):
  227. remove = [svc for svc in self.active_services if svc not in services]
  228. add = [svc for svc in services if svc not in self.active_services]
  229. self.remove_services_from_charts(remove)
  230. self.add_services_to_charts(add)
  231. self.active_services = services
  232. def add_services_to_charts(self, services):
  233. for svc in services:
  234. if svc.type == TYPE_HOST:
  235. self.charts['host_latency'].add_dimension([svc.latency_key(), svc.name, 'absolute', 1000, 1000000])
  236. if svc.type == TYPE_PROCESS:
  237. self.charts['process_uptime'].add_dimension([svc.uptime_key(), svc.name])
  238. self.charts['process_threads'].add_dimension([svc.threads_key(), svc.name])
  239. self.charts['process_children'].add_dimension([svc.children_key(), svc.name])
  240. self.charts[svc.type.name].add_dimension([svc.key(), svc.name])
  241. def remove_services_from_charts(self, services):
  242. for svc in services:
  243. if svc.type == TYPE_HOST:
  244. self.charts['host_latency'].del_dimension(svc.latency_key(), False)
  245. if svc.type == TYPE_PROCESS:
  246. self.charts['process_uptime'].del_dimension(svc.uptime_key(), False)
  247. self.charts['process_threads'].del_dimension(svc.threads_key(), False)
  248. self.charts['process_children'].del_dimension(svc.children_key(), False)
  249. self.charts[svc.type.name].del_dimension(svc.key(), False)
  250. def create_service(root, typ):
  251. if typ == TYPE_HOST:
  252. return create_host_service(root)
  253. elif typ == TYPE_PROCESS:
  254. return create_process_service(root)
  255. return create_base_service(root, typ)
  256. def create_host_service(root):
  257. svc = HostMonitService(
  258. TYPE_HOST,
  259. root.find('name').text,
  260. root.find('status').text,
  261. root.find('monitor').text,
  262. )
  263. latency = root.find('./icmp/responsetime')
  264. if latency is not None:
  265. svc.latency = latency.text
  266. return svc
  267. def create_process_service(root):
  268. svc = ProcessMonitService(
  269. TYPE_PROCESS,
  270. root.find('name').text,
  271. root.find('status').text,
  272. root.find('monitor').text,
  273. )
  274. uptime = root.find('uptime')
  275. if uptime is not None:
  276. svc.uptime = uptime.text
  277. threads = root.find('threads')
  278. if threads is not None:
  279. svc.threads = threads.text
  280. children = root.find('children')
  281. if children is not None:
  282. svc.children = children.text
  283. return svc
  284. def create_base_service(root, typ):
  285. return BaseMonitService(
  286. typ,
  287. root.find('name').text,
  288. root.find('status').text,
  289. root.find('monitor').text,
  290. )