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.

156 lines
5.3 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2015 ABF OSIELL <http://osiell.com>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import logging
  5. import os
  6. import shlex
  7. import subprocess
  8. from openerp import _, api, fields, models
  9. from openerp.exceptions import Warning as UserError
  10. from openerp.addons.base.ir.ir_cron import str2tuple
  11. _logger = logging.getLogger(__name__)
  12. SEND_NSCA_BIN = '/usr/sbin/send_nsca'
  13. def is_exe(fpath):
  14. return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
  15. class NscaCheck(models.Model):
  16. _name = "nsca.check"
  17. _description = u"NSCA Check"
  18. _inherits = {'ir.cron': 'cron_id'}
  19. cron_id = fields.Many2one(
  20. 'ir.cron', string=u"Cron",
  21. required=True, ondelete='cascade', readonly=True)
  22. server_id = fields.Many2one(
  23. 'nsca.server', string=u"Server", required=True)
  24. service = fields.Char(u"Service", required=True)
  25. nsca_model = fields.Char(u"Model")
  26. nsca_function = fields.Char(u"Method")
  27. nsca_args = fields.Char(u"Arguments")
  28. allow_void_result = fields.Boolean(
  29. u"Allow void result", default=False,
  30. help=u"By default, a CRITICAL message is sent if the method does not "
  31. u"return.\nIf checked, no message will be sent in such a case.")
  32. @api.model
  33. def default_get(self, fields_list):
  34. """Set some default values on the fly, without overriding fields (which
  35. has the side effect to re-create the fields on the current model).
  36. """
  37. res = super(NscaCheck, self).default_get(fields_list)
  38. NscaServer = self.env['nsca.server']
  39. res['name'] = 'TEMP' # Required on 'ir.cron', replaced later
  40. res['interval_number'] = 10
  41. res['interval_type'] = 'minutes'
  42. res['server_id'] = NscaServer.search([])[0].id
  43. return res
  44. @api.multi
  45. def _force_values(self):
  46. """Force some values:
  47. - Compute the name of the NSCA check to be readable
  48. among the others 'ir.cron' records.
  49. """
  50. for check in self:
  51. vals = {
  52. 'name': u"%s - %s" % (_(u"NSCA Check"), check.service),
  53. 'model': self._name,
  54. 'function': '_cron_check',
  55. 'args': '(%s,)' % check.id,
  56. 'doall': False,
  57. 'numbercall': -1
  58. }
  59. super(NscaCheck, check).write(vals)
  60. @api.model
  61. def create(self, vals):
  62. check = super(NscaCheck, self).create(vals)
  63. check._force_values()
  64. return check
  65. @api.multi
  66. def write(self, vals):
  67. res = super(NscaCheck, self).write(vals)
  68. if 'service' in vals:
  69. self._force_values()
  70. return res
  71. @api.model
  72. def _cron_check(self, check_id):
  73. self._check_send_nsca_command()
  74. check = self.browse(check_id)
  75. rc, message = 3, "Unknown"
  76. try:
  77. args = str2tuple(check.nsca_args)
  78. NscaModel = self.env[check.nsca_model]
  79. result = getattr(NscaModel, check.nsca_function)(*args)
  80. if not result:
  81. if check.allow_void_result:
  82. return False
  83. raise ValueError(
  84. "'%s' method does not return" % check.nsca_function)
  85. rc, message = result
  86. except Exception, exc:
  87. rc, message = 2, "%s" % exc
  88. _logger.error("%s - %s", check.service, message)
  89. check._send_nsca(rc, message)
  90. return True
  91. @api.multi
  92. def _send_nsca(self, rc, message):
  93. """Send the result of the check to the NSCA daemon."""
  94. for check in self:
  95. check_result = self._format_check_result(check, rc, message)
  96. cmd = self._prepare_command(check)
  97. self._run_command(check, cmd, check_result)
  98. @api.model
  99. def _format_check_result(self, check, rc, message):
  100. """Format the check result with tabulations as delimiter."""
  101. message = message.replace('\t', ' ')
  102. hostname = check.server_id.node_hostname
  103. check_result = u"%s\t%s\t%s\t%s" % (
  104. hostname, check.service, rc, message)
  105. return check_result.encode('utf-8')
  106. @api.model
  107. def _prepare_command(self, check):
  108. """Prepare the shell command used to send the check result
  109. to the NSCA daemon.
  110. """
  111. cmd = u"/usr/sbin/send_nsca -H %s -p %s -c %s" % (
  112. check.server_id.name,
  113. check.server_id.port,
  114. check.server_id.config_file_path)
  115. return shlex.split(cmd)
  116. @api.model
  117. def _run_command(self, check, cmd, check_result):
  118. """Send the check result through the '/usr/sbin/send_nsca' command."""
  119. try:
  120. proc = subprocess.Popen(
  121. cmd,
  122. stdout=subprocess.PIPE,
  123. stdin=subprocess.PIPE,
  124. stderr=subprocess.STDOUT)
  125. stdout = proc.communicate(
  126. input=check_result)[0]
  127. _logger.info("%s: %s", check_result, stdout.strip())
  128. except Exception, exc:
  129. _logger.error(exc)
  130. def _check_send_nsca_command(self):
  131. """Check if the NSCA client is installed."""
  132. if not is_exe(SEND_NSCA_BIN):
  133. raise UserError(
  134. _(u"Command '%s' not found. Please install the NSCA client.\n"
  135. u"On Debian/Ubuntu: apt-get install nsca-client") % (
  136. SEND_NSCA_BIN))