You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
8.2 KiB

  1. # (Copyright) 2015 ABF OSIELL <http://osiell.com>
  2. # (Copyright) 2018 Creu Blanca
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import psutil
  5. import os
  6. import shlex
  7. import subprocess
  8. import logging
  9. from odoo import api, fields, models, _
  10. from odoo.tools import config
  11. from odoo.exceptions import UserError
  12. def is_exe(fpath):
  13. return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
  14. _logger = logging.getLogger(__name__)
  15. SEND_NSCA_BIN = '/usr/sbin/send_nsca'
  16. class NscaServer(models.Model):
  17. _name = "nsca.server"
  18. _description = u"NSCA Server"
  19. name = fields.Char(u"Hostname", required=True)
  20. port = fields.Integer(u"Port", default=5667, required=True)
  21. password = fields.Char(u"Password")
  22. encryption_method = fields.Selection(
  23. selection='_selection_encryption_method',
  24. string=u"Encryption method", default='1', required=True)
  25. config_dir_path = fields.Char(
  26. u"Configuration directory",
  27. compute='_compute_config_dir_path')
  28. config_file_path = fields.Char(
  29. u"Configuration file",
  30. compute='_compute_config_file_path')
  31. node_hostname = fields.Char(
  32. u"Hostname of this node", required=True,
  33. help=u"This is the hostname of the current Odoo node declared in the "
  34. u"monitoring server.")
  35. check_ids = fields.One2many(
  36. 'nsca.check', 'server_id', string=u"Checks")
  37. check_count = fields.Integer(
  38. compute='_compute_check_count'
  39. )
  40. @api.depends('check_ids')
  41. def _compute_check_count(self):
  42. for r in self:
  43. r.check_count = len(r.check_ids)
  44. def _selection_encryption_method(self):
  45. return [
  46. ('0', u"0 - None (Do NOT use this option)"),
  47. ('1', u"1 - Simple XOR"),
  48. ('2', u"2 - DES"),
  49. ('3', u"3 - 3DES (Triple DES)"),
  50. ('4', u"4 - CAST-128"),
  51. ('5', u"5 - CAST-256"),
  52. ('6', u"6 - xTEA"),
  53. ('7', u"7 - 3WAY"),
  54. ('8', u"8 - BLOWFISH"),
  55. ('9', u"9 - TWOFISH"),
  56. ('10', u"10 - LOKI97"),
  57. ('11', u"11 - RC2"),
  58. ('12', u"12 - ARCFOUR"),
  59. ('14', u"14 - RIJNDAEL-128"),
  60. ('15', u"15 - RIJNDAEL-192"),
  61. ('16', u"16 - RIJNDAEL-256"),
  62. ('19', u"19 - WAKE"),
  63. ('20', u"20 - SERPENT"),
  64. ('22', u"22 - ENIGMA (Unix crypt)"),
  65. ('23', u"23 - GOST"),
  66. ('24', u"24 - SAFER64"),
  67. ('25', u"25 - SAFER128"),
  68. ('26', u"26 - SAFER+"),
  69. ]
  70. @api.multi
  71. def _compute_config_dir_path(self):
  72. for server in self:
  73. data_dir_path = config.get('data_dir')
  74. dir_path = os.path.join(
  75. data_dir_path, 'nsca_client', self.env.cr.dbname)
  76. server.config_dir_path = dir_path
  77. @api.multi
  78. def _compute_config_file_path(self):
  79. for server in self:
  80. file_name = 'send_nsca_%s.cfg' % server.id
  81. full_path = os.path.join(server.config_dir_path, file_name)
  82. server.config_file_path = full_path
  83. @api.multi
  84. def write_config_file(self):
  85. for server in self:
  86. try:
  87. os.makedirs(server.config_dir_path)
  88. except OSError as exception:
  89. if exception.errno != os.errno.EEXIST:
  90. raise
  91. with open(server.config_file_path, 'w') as config_file:
  92. if server.password:
  93. config_file.write('password=%s\n' % server.password)
  94. config_file.write(
  95. 'encryption_method=%s\n' % server.encryption_method)
  96. return True
  97. @api.multi
  98. def write(self, vals):
  99. res = super(NscaServer, self).write(vals)
  100. self.write_config_file()
  101. return res
  102. @api.model
  103. def create(self, vals):
  104. res = super(NscaServer, self).create(vals)
  105. res.write_config_file()
  106. return res
  107. @api.model
  108. def current_status(self):
  109. ram = 0
  110. cpu = 0
  111. if psutil:
  112. process = psutil.Process(os.getpid())
  113. # psutil changed its api through versions
  114. processes = [process]
  115. if config.get(
  116. 'workers') and process.parent: # pragma: no cover
  117. if hasattr(process.parent, '__call__'):
  118. process = process.parent()
  119. else:
  120. process = process.parent
  121. if hasattr(process, 'children'):
  122. processes += process.children(True)
  123. elif hasattr(process, 'get_children'):
  124. processes += process.get_children(True)
  125. for process in processes:
  126. if hasattr(process, 'memory_percent'):
  127. ram += process.memory_percent()
  128. if hasattr(process, 'cpu_percent'):
  129. cpu += process.cpu_percent(interval=1)
  130. user_count = 0
  131. if 'bus.presence' in self.env.registry:
  132. user_count = self.env['bus.presence'].search_count([
  133. ('status', '=', 'online'),
  134. ])
  135. performance = {
  136. 'cpu': {
  137. 'value': cpu,
  138. },
  139. 'ram': {
  140. 'value': ram,
  141. },
  142. 'user_count': {
  143. 'value': user_count,
  144. },
  145. }
  146. return 0, u"OK", performance
  147. @api.multi
  148. def _prepare_command(self):
  149. """Prepare the shell command used to send the check result
  150. to the NSCA daemon.
  151. """
  152. cmd = u"/usr/sbin/send_nsca -H %s -p %s -c %s" % (
  153. self.name,
  154. self.port,
  155. self.config_file_path)
  156. return shlex.split(cmd)
  157. @api.model
  158. def _run_command(self, cmd, check_result):
  159. """Send the check result through the '/usr/sbin/send_nsca' command."""
  160. try:
  161. proc = subprocess.Popen(
  162. cmd,
  163. stdout=subprocess.PIPE,
  164. stdin=subprocess.PIPE,
  165. stderr=subprocess.STDOUT)
  166. stdout = proc.communicate(
  167. input=check_result)[0]
  168. _logger.debug("%s: %s", check_result, stdout.strip())
  169. except Exception as exc:
  170. _logger.error(exc)
  171. def _check_send_nsca_command(self):
  172. """Check if the NSCA client is installed."""
  173. if not is_exe(SEND_NSCA_BIN):
  174. raise UserError(
  175. _(u"Command '%s' not found. Please install the NSCA client.\n"
  176. u"On Debian/Ubuntu: apt-get install nsca-client") % (
  177. SEND_NSCA_BIN))
  178. def _format_check_result(self, service, rc, message):
  179. """Format the check result with tabulations as delimiter."""
  180. message = message.replace('\t', ' ')
  181. hostname = self.node_hostname
  182. check_result = u"%s\t%s\t%s\t%s" % (
  183. hostname, service, rc, message)
  184. return check_result.encode('utf-8')
  185. def _send_nsca(self, service, rc, message, performance):
  186. """Send the result of the check to the NSCA daemon."""
  187. msg = message
  188. if len(performance) > 0:
  189. msg += '| ' + ''.join(
  190. ["%s=%s%s;%s;%s;%s;%s" % (
  191. key,
  192. performance[key]['value'],
  193. performance[key].get('uom', ''),
  194. performance[key].get('warn', ''),
  195. performance[key].get('crit', ''),
  196. performance[key].get('min', ''),
  197. performance[key].get('max', ''),
  198. ) for key in sorted(performance)])
  199. check_result = self._format_check_result(
  200. service, rc, msg)
  201. cmd = self._prepare_command()
  202. self._run_command(cmd, check_result)
  203. @api.multi
  204. def show_checks(self):
  205. self.ensure_one()
  206. action = self.env.ref('nsca_client.action_nsca_check_tree')
  207. result = action.read()[0]
  208. context = {'default_server_id': self.id}
  209. result['context'] = context
  210. result['domain'] = [('server_id', '=', self.id)]
  211. if len(self.check_ids) == 1:
  212. res = self.env.ref('nsca_client.view_nsca_check_form', False)
  213. result['views'] = [(res and res.id or False, 'form')]
  214. result['res_id'] = self.check_ids.id
  215. return result