From e9fd5269982e7b9d924e1450236cae585f206571 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Thu, 7 Jun 2018 13:08:05 +0200 Subject: [PATCH] [MIG] nsca_client --- .travis.yml | 1 + nsca_client/README.rst | 41 +++-- nsca_client/__init__.py | 1 - .../{__openerp__.py => __manifest__.py} | 6 +- nsca_client/data/nsca_server.xml | 14 -- nsca_client/demo/demo_data.xml | 32 ++-- nsca_client/models/__init__.py | 4 - nsca_client/models/nsca_check.py | 68 +++++--- nsca_client/models/nsca_server.py | 52 +++++- nsca_client/tests/__init__.py | 1 + nsca_client/tests/test_nsca.py | 97 +++++++++++ nsca_client/views/nsca_check.xml | 151 ++++++++++-------- nsca_client/views/nsca_menu.xml | 4 +- nsca_client/views/nsca_server.xml | 94 +++++------ 14 files changed, 368 insertions(+), 198 deletions(-) rename nsca_client/{__openerp__.py => __manifest__.py} (82%) delete mode 100644 nsca_client/data/nsca_server.xml create mode 100644 nsca_client/tests/__init__.py create mode 100644 nsca_client/tests/test_nsca.py diff --git a/.travis.yml b/.travis.yml index 614e5e0df..2b3375599 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ addons: packages: - expect-dev # provides unbuffer utility - python-lxml # because pip installation is slow + - nsca-client env: global: diff --git a/nsca_client/README.rst b/nsca_client/README.rst index 25a4facad..381db2e6a 100644 --- a/nsca_client/README.rst +++ b/nsca_client/README.rst @@ -40,14 +40,28 @@ To configure this module, you need to: * Code the methods which will be called by the NSCA checks. -Such methods must return a tuple ``(RC, MESSAGE)`` where ``RC`` is an integer, -and ``MESSAGE`` a unicode string. ``RC`` values and the corresponding status are: +Such methods must return a tuple ``(RC, MESSAGE, PERFORMANCE_DATA)`` where ``RC`` is an integer, +``MESSAGE`` a unicode string AND ``PERFOMANCE_DATA`` is a dictionary. +``RC`` values and the corresponding status are: - 0: OK - 1: WARNING - 2: CRITICAL - 3: UNKNOWN +``PERFORMANCE_DATA`` is not mandatory, so it could be possible to send +``(RC, MESSAGE)``. +Each element of ``PERFORMANCE_DATA`` will be a dictionary that could contain: + +- value: value of the data (required) +- max: Max value on the chart +- min: Minimum value on the chart +- warn: Warning value on the chart +- crit: Critical value on the chart +- uom: Unit of Measure on the chart (s - Seconds, % - Percentage, B - Bytes, c - Continuous) + +The key of the dictionary will be used as the performance_data label. + E.g: .. code-block:: python @@ -59,20 +73,19 @@ E.g: def nsca_check_mails(self): mails = self.search([('state', '=', 'exception')]) if mails: - return (1, u"%s mails not sent" % len(mails)) - return (0, u"OK") + return (1, u"%s mails not sent" % len(mails), { + 'exceptions': {'value': len(mails)}}) + return (0, u"OK", {'exceptions': {'value': len(mails)}}) + +On the example, the performance data will use the label ``exceptions`` and the +value will be the number of exception of mails. Usage ===== .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/149/8.0 - -Known issues / Roadmap -====================== - -* Send performance data + :target: https://runbot.odoo-community.org/runbot/149/11.0 Bug Tracker =========== @@ -80,11 +93,8 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, -help us smashing it by providing a detailed and welcomed `feedback -`_. +help us smashing it by providing a detailed and welcomed feedback. + Credits ======= @@ -98,6 +108,7 @@ Contributors ------------ * Sébastien Alix +* Enric Tobella Maintainer ---------- diff --git a/nsca_client/__init__.py b/nsca_client/__init__.py index f71417703..b9367d7f2 100644 --- a/nsca_client/__init__.py +++ b/nsca_client/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # © 2015 ABF OSIELL # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). diff --git a/nsca_client/__openerp__.py b/nsca_client/__manifest__.py similarity index 82% rename from nsca_client/__openerp__.py rename to nsca_client/__manifest__.py index 5571fb51d..0aebc1cea 100644 --- a/nsca_client/__openerp__.py +++ b/nsca_client/__manifest__.py @@ -1,19 +1,17 @@ -# -*- coding: utf-8 -*- # © 2015 ABF OSIELL # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "NSCA Client", "summary": "Send passive alerts to monitor your Odoo application.", - "version": "8.0.1.0.0", + "version": "11.0.1.0.0", "category": "Tools", - "website": "http://osiell.com/", + "website": "http://github.com/OCA/server-tools", "author": "ABF OSIELL, Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, "installable": True, "data": [ "security/ir.model.access.csv", - "data/nsca_server.xml", "views/nsca_menu.xml", "views/nsca_check.xml", "views/nsca_server.xml", diff --git a/nsca_client/data/nsca_server.xml b/nsca_client/data/nsca_server.xml deleted file mode 100644 index bcc61939d..000000000 --- a/nsca_client/data/nsca_server.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - nagios.example.net - 5667 - MY-SERVER - - - - diff --git a/nsca_client/demo/demo_data.xml b/nsca_client/demo/demo_data.xml index 2c9f91b18..ae53551e0 100644 --- a/nsca_client/demo/demo_data.xml +++ b/nsca_client/demo/demo_data.xml @@ -1,16 +1,22 @@ - - + + - - - Odoo Mail Queue - - minutes - mail.mail - nsca_check_mails - - + + nagios.example.net + 5667 + MY-SERVER + - - + + + Odoo Mail Queue + + minutes + mail.mail + nsca_check_mails + + + + + diff --git a/nsca_client/models/__init__.py b/nsca_client/models/__init__.py index 32927f70a..b1b898df9 100644 --- a/nsca_client/models/__init__.py +++ b/nsca_client/models/__init__.py @@ -1,5 +1 @@ -# -*- coding: utf-8 -*- -# © 2015 ABF OSIELL -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - from . import nsca_check, nsca_server diff --git a/nsca_client/models/nsca_check.py b/nsca_client/models/nsca_check.py index 079d9df8a..c846d881e 100644 --- a/nsca_client/models/nsca_check.py +++ b/nsca_client/models/nsca_check.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- -# © 2015 ABF OSIELL +# (Copyright) 2015 ABF OSIELL +# (Copyright) 2018 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging @@ -7,10 +7,9 @@ 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 +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) @@ -46,11 +45,9 @@ class NscaCheck(models.Model): 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['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 @@ -59,12 +56,13 @@ class NscaCheck(models.Model): - Compute the name of the NSCA check to be readable among the others 'ir.cron' records. """ + model = self.env['ir.model'].search([('model', '=', self._name)]) for check in self: vals = { 'name': u"%s - %s" % (_(u"NSCA Check"), check.service), - 'model': self._name, - 'function': '_cron_check', - 'args': '(%s,)' % check.id, + 'model_id': model.id, + 'state': 'code', + 'code': 'model._cron_check(%s,)' % check.id, 'doall': False, 'numbercall': -1 } @@ -72,6 +70,11 @@ class NscaCheck(models.Model): @api.model def create(self, vals): + if not vals.get('model_id', False): + vals['model_id'] = self.env['ir.model'].search([ + ('model', '=', self._name)]).id + if not vals.get('state', False): + vals['state'] = 'code' check = super(NscaCheck, self).create(vals) check._force_values() return check @@ -87,28 +90,47 @@ class NscaCheck(models.Model): def _cron_check(self, check_id): self._check_send_nsca_command() check = self.browse(check_id) - rc, message = 3, "Unknown" + rc, message, performance = 3, "Unknown", {} try: - args = str2tuple(check.nsca_args) NscaModel = self.env[check.nsca_model] - result = getattr(NscaModel, check.nsca_function)(*args) + results = {'model': NscaModel} + safe_eval( + 'result = model.%s(%s)' % ( + check.nsca_function, check.nsca_args or ''), + results, mode="exec", nocopy=True) + result = results['result'] 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: + if len(result) == 2: + rc, message = result + else: + rc, message, performance = result + except Exception as exc: rc, message = 2, "%s" % exc - _logger.error("%s - %s", check.service, message) - check._send_nsca(rc, message) + _logger.warning("%s - %s", check.service, message) + check._send_nsca(rc, message, performance) return True @api.multi - def _send_nsca(self, rc, message): + def _send_nsca(self, rc, message, performance): """Send the result of the check to the NSCA daemon.""" for check in self: - check_result = self._format_check_result(check, rc, message) + msg = message + if len(performance) > 0: + msg += '| ' + ''.join( + ["%s=%s%s;%s;%s;%s;%s" % ( + key, + performance[key]['value'], + performance[key].get('uom', ''), + performance[key].get('warn', ''), + performance[key].get('crit', ''), + performance[key].get('min', ''), + performance[key].get('max', ''), + ) for key in sorted(performance)]) + check_result = self._format_check_result(check, rc, msg) cmd = self._prepare_command(check) self._run_command(check, cmd, check_result) @@ -143,8 +165,8 @@ class NscaCheck(models.Model): stderr=subprocess.STDOUT) stdout = proc.communicate( input=check_result)[0] - _logger.info("%s: %s", check_result, stdout.strip()) - except Exception, exc: + _logger.debug("%s: %s", check_result, stdout.strip()) + except Exception as exc: _logger.error(exc) def _check_send_nsca_command(self): diff --git a/nsca_client/models/nsca_server.py b/nsca_client/models/nsca_server.py index 77fbad9a6..5fed760c8 100644 --- a/nsca_client/models/nsca_server.py +++ b/nsca_client/models/nsca_server.py @@ -1,11 +1,12 @@ -# -*- coding: utf-8 -*- -# © 2015 ABF OSIELL -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# (Copyright) 2015 ABF OSIELL +# (Copyright) 2018 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import psutil import os -from openerp import api, fields, models -from openerp.tools import config +from odoo import api, fields, models +from odoo.tools import config class NscaServer(models.Model): @@ -99,3 +100,44 @@ class NscaServer(models.Model): res = super(NscaServer, self).create(vals) res.write_config_file() return res + + @api.model + def current_status(self): + ram = 0 + cpu = 0 + if psutil: + process = psutil.Process(os.getpid()) + # psutil changed its api through versions + processes = [process] + if config.get( + 'workers') and process.parent: # pragma: no cover + if hasattr(process.parent, '__call__'): + process = process.parent() + else: + process = process.parent + if hasattr(process, 'children'): + processes += process.children(True) + elif hasattr(process, 'get_children'): + processes += process.get_children(True) + for process in processes: + if hasattr(process, 'memory_percent'): + ram += process.memory_percent() + if hasattr(process, 'cpu_percent'): + cpu += process.cpu_percent(interval=1) + user_count = 0 + if 'bus.presence' in self.env.registry: + user_count = self.env['bus.presence'].search_count([ + ('status', '=', 'online'), + ]) + performance = { + 'cpu': { + 'value': cpu, + }, + 'ram': { + 'value': ram, + }, + 'user_count': { + 'value': user_count, + }, + } + return 0, u"OK", performance diff --git a/nsca_client/tests/__init__.py b/nsca_client/tests/__init__.py new file mode 100644 index 000000000..3cc2c357b --- /dev/null +++ b/nsca_client/tests/__init__.py @@ -0,0 +1 @@ +from . import test_nsca diff --git a/nsca_client/tests/test_nsca.py b/nsca_client/tests/test_nsca.py new file mode 100644 index 000000000..bf797d561 --- /dev/null +++ b/nsca_client/tests/test_nsca.py @@ -0,0 +1,97 @@ +# Copyright 2018 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import mock + +from odoo.tests.common import TransactionCase + + +class Popen: + def __init__(self, cmd, stdout, stdin, stderr): + self.cmd = cmd + self.stdout = stdout + self.stdin = stdin + self.stderr = stderr + + def communicate(input): + return ['test'] + + +class TestNsca(TransactionCase): + + def test_nsca(self): + server = self.env['nsca.server'].create({ + 'name': 'localhost', + 'password': 'pass', + 'encryption_method': '3', + 'node_hostname': 'odoodev', + }) + self.assertTrue(server.config_file_path) + with mock.patch('subprocess.Popen') as post: + post.return_value = Popen + check = self.env['nsca.check'].create({ + 'server_id': server.id, + 'service': 'test', + 'nsca_model': 'nsca.server', + 'nsca_function': 'current_status' + }) + self.assertTrue(check.model_id) + self.env['nsca.check']._cron_check(check.id,) + + def test_write(self): + server = self.env['nsca.server'].create({ + 'name': 'localhost', + 'password': 'pass', + 'encryption_method': '3', + 'node_hostname': 'odoodev', + }) + self.assertTrue(server.config_file_path) + check = self.env['nsca.check'].create({ + 'server_id': server.id, + 'service': 'test', + 'nsca_model': 'nsca.server', + 'nsca_function': 'current_status' + }) + check.cron_id.state = 'object_create' + check.write({'interval_number': 1}) + self.assertEqual(check.cron_id.state, 'object_create') + check.write({'service': 'change'}) + self.assertNotEqual(check.cron_id.state, 'object_create') + + def test_void_failure(self): + server = self.env['nsca.server'].create({ + 'name': 'localhost', + 'password': 'pass', + 'encryption_method': '3', + 'node_hostname': 'odoodev', + }) + check = self.env['nsca.check'].create({ + 'server_id': server.id, + 'service': 'test', + 'nsca_model': 'nsca.check', + 'allow_void_result': False, + 'nsca_function': '_check_send_nsca_command' + }) + with mock.patch('subprocess.Popen') as post: + post.return_value = Popen + self.env['nsca.check']._cron_check(check.id,) + post.assert_called_once() + + def test_void_ok(self): + server = self.env['nsca.server'].create({ + 'name': 'localhost', + 'password': 'pass', + 'encryption_method': '3', + 'node_hostname': 'odoodev', + }) + check = self.env['nsca.check'].create({ + 'server_id': server.id, + 'service': 'test', + 'nsca_model': 'nsca.check', + 'allow_void_result': True, + 'nsca_function': '_check_send_nsca_command' + }) + with mock.patch('subprocess.Popen') as post: + post.return_value = Popen + self.env['nsca.check']._cron_check(check.id,) + post.assert_not_called() diff --git a/nsca_client/views/nsca_check.xml b/nsca_client/views/nsca_check.xml index ce2f29cf9..9713d9cf3 100644 --- a/nsca_client/views/nsca_check.xml +++ b/nsca_client/views/nsca_check.xml @@ -2,78 +2,89 @@ - - + + - - nsca.check.form - nsca.check - -
- - - - - - - - - - - -
-

The method must return a tuple (RC, MESSAGE) where RC is an integer:

- -
    -
  • 0: OK
  • -
  • 1: WARNING
  • -
  • 2: CRITICAL
  • -
  • 3: UNKNOWN
  • -
- -

Any other RC value will be treated as CRITICAL.

-

E.g. (1, u"3 mails not sent")

-
-
-
-
-
-
-
+ + nsca.check.form + nsca.check + +
+ + + + + + + + + + + +
+

The method must return a tuple (RC, + MESSAGE) where RC is an integer: +

+ +
    +
  • 0: OK
  • +
  • 1: WARNING
  • +
  • 2: CRITICAL
  • +
  • 3: UNKNOWN
  • +
+ +

Any other RC value will be treated as + CRITICAL. +

+

E.g. + (1, u"3 mails not sent") +

+
+
+
+
+
+
+
- - nsca.check.tree - nsca.check - - - - - - - - - + + nsca.check.tree + nsca.check + + + + + + + + + - - Checks - ir.actions.act_window - nsca.check - form - - - ['|', ('active', '=', True), ('active', '=', False)] - + + Checks + ir.actions.act_window + nsca.check + form + + + ['|', ('active', '=', True), ('active', '=', + False)] + + - + -
-
+ + diff --git a/nsca_client/views/nsca_menu.xml b/nsca_client/views/nsca_menu.xml index 4472e82c5..1a35df036 100644 --- a/nsca_client/views/nsca_menu.xml +++ b/nsca_client/views/nsca_menu.xml @@ -1,7 +1,7 @@ - + - + diff --git a/nsca_client/views/nsca_server.xml b/nsca_client/views/nsca_server.xml index db725a51d..145ba0d35 100644 --- a/nsca_client/views/nsca_server.xml +++ b/nsca_client/views/nsca_server.xml @@ -2,56 +2,56 @@ - - + + - - nsca.server.form - nsca.server - -
- - - + + nsca.server.form + nsca.server + + + + + + + + + + + + + + + + + + + + + + + nsca.server.tree + nsca.server + + + - - - - - - - - - -
- -
-
- - - nsca.server.tree - nsca.server - - - - - - - - + + + - - Servers - ir.actions.act_window - nsca.server - form - - + + Servers + ir.actions.act_window + nsca.server + form + + - + -
-
+ +