# -*- coding: utf-8 -*- # © 2015 ABF OSIELL # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging import os import shlex import subprocess from openerp import _, api, fields, models from openerp.exceptions import Warning as UserError from openerp.addons.base.ir.ir_cron import str2tuple _logger = logging.getLogger(__name__) SEND_NSCA_BIN = '/usr/sbin/send_nsca' def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) class NscaCheck(models.Model): _name = "nsca.check" _description = u"NSCA Check" _inherits = {'ir.cron': 'cron_id'} cron_id = fields.Many2one( 'ir.cron', string=u"Cron", required=True, ondelete='cascade', readonly=True) server_id = fields.Many2one( 'nsca.server', string=u"Server", required=True) service = fields.Char(u"Service", required=True) nsca_model = fields.Char(u"Model") nsca_function = fields.Char(u"Method") nsca_args = fields.Char(u"Arguments") allow_void_result = fields.Boolean( u"Allow void result", default=False, help=u"By default, a CRITICAL message is sent if the method does not " u"return.\nIf checked, no message will be sent in such a case.") @api.model def default_get(self, fields_list): """Set some default values on the fly, without overriding fields (which has the side effect to re-create the fields on the current model). """ res = super(NscaCheck, self).default_get(fields_list) NscaServer = self.env['nsca.server'] res['name'] = 'TEMP' # Required on 'ir.cron', replaced later res['interval_number'] = 10 res['interval_type'] = 'minutes' res['server_id'] = NscaServer.search([])[0].id return res @api.multi def _force_values(self): """Force some values: - Compute the name of the NSCA check to be readable among the others 'ir.cron' records. """ for check in self: vals = { 'name': u"%s - %s" % (_(u"NSCA Check"), check.service), 'model': self._name, 'function': '_cron_check', 'args': '(%s,)' % check.id, 'doall': False, 'numbercall': -1 } super(NscaCheck, check).write(vals) @api.model def create(self, vals): check = super(NscaCheck, self).create(vals) check._force_values() return check @api.multi def write(self, vals): res = super(NscaCheck, self).write(vals) if 'service' in vals: self._force_values() return res @api.model def _cron_check(self, check_id): self._check_send_nsca_command() check = self.browse(check_id) rc, message = 3, "Unknown" try: args = str2tuple(check.nsca_args) NscaModel = self.env[check.nsca_model] result = getattr(NscaModel, check.nsca_function)(*args) if not result: if check.allow_void_result: return False raise ValueError( "'%s' method does not return" % check.nsca_function) rc, message = result except Exception, exc: rc, message = 2, "%s" % exc _logger.error("%s - %s", check.service, message) check._send_nsca(rc, message) return True @api.multi def _send_nsca(self, rc, message): """Send the result of the check to the NSCA daemon.""" for check in self: check_result = self._format_check_result(check, rc, message) cmd = self._prepare_command(check) self._run_command(check, cmd, check_result) @api.model def _format_check_result(self, check, rc, message): """Format the check result with tabulations as delimiter.""" message = message.replace('\t', ' ') hostname = check.server_id.node_hostname check_result = u"%s\t%s\t%s\t%s" % ( hostname, check.service, rc, message) return check_result.encode('utf-8') @api.model def _prepare_command(self, check): """Prepare the shell command used to send the check result to the NSCA daemon. """ cmd = u"/usr/sbin/send_nsca -H %s -p %s -c %s" % ( check.server_id.name, check.server_id.port, check.server_id.config_file_path) return shlex.split(cmd) @api.model def _run_command(self, check, cmd, check_result): """Send the check result through the '/usr/sbin/send_nsca' command.""" try: proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = proc.communicate( input=check_result)[0] _logger.info("%s: %s", check_result, stdout.strip()) except Exception, exc: _logger.error(exc) def _check_send_nsca_command(self): """Check if the NSCA client is installed.""" if not is_exe(SEND_NSCA_BIN): raise UserError( _(u"Command '%s' not found. Please install the NSCA client.\n" u"On Debian/Ubuntu: apt-get install nsca-client") % ( SEND_NSCA_BIN))