Browse Source

[MIG] nsca_client

pull/1280/head
Enric Tobella 7 years ago
parent
commit
e9fd526998
No known key found for this signature in database GPG Key ID: 1A2546A1B7BA2451
  1. 1
      .travis.yml
  2. 41
      nsca_client/README.rst
  3. 1
      nsca_client/__init__.py
  4. 6
      nsca_client/__manifest__.py
  5. 14
      nsca_client/data/nsca_server.xml
  6. 12
      nsca_client/demo/demo_data.xml
  7. 4
      nsca_client/models/__init__.py
  8. 64
      nsca_client/models/nsca_check.py
  9. 50
      nsca_client/models/nsca_server.py
  10. 1
      nsca_client/tests/__init__.py
  11. 97
      nsca_client/tests/test_nsca.py
  12. 33
      nsca_client/views/nsca_check.xml
  13. 4
      nsca_client/views/nsca_menu.xml
  14. 4
      nsca_client/views/nsca_server.xml

1
.travis.yml

@ -12,6 +12,7 @@ addons:
packages: packages:
- expect-dev # provides unbuffer utility - expect-dev # provides unbuffer utility
- python-lxml # because pip installation is slow - python-lxml # because pip installation is slow
- nsca-client
env: env:
global: global:

41
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. * 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 - 0: OK
- 1: WARNING - 1: WARNING
- 2: CRITICAL - 2: CRITICAL
- 3: UNKNOWN - 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: E.g:
.. code-block:: python .. code-block:: python
@ -59,20 +73,19 @@ E.g:
def nsca_check_mails(self): def nsca_check_mails(self):
mails = self.search([('state', '=', 'exception')]) mails = self.search([('state', '=', 'exception')])
if mails: 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 Usage
===== =====
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot :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 Bug Tracker
=========== ===========
@ -80,11 +93,8 @@ Bug Tracker
Bugs are tracked on `GitHub Issues Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please <https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first, 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
<https://github.com/OCA/
server-tools/issues/new?body=module:%20
nsca_client%0Aversion:%20
8.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
help us smashing it by providing a detailed and welcomed feedback.
Credits Credits
======= =======
@ -98,6 +108,7 @@ Contributors
------------ ------------
* Sébastien Alix <sebastien.alix@osiell.com> * Sébastien Alix <sebastien.alix@osiell.com>
* Enric Tobella <etobella@creublanca.es>
Maintainer Maintainer
---------- ----------

1
nsca_client/__init__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com> # © 2015 ABF OSIELL <http://osiell.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

6
nsca_client/__openerp__.py → nsca_client/__manifest__.py

@ -1,19 +1,17 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com> # © 2015 ABF OSIELL <http://osiell.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{ {
"name": "NSCA Client", "name": "NSCA Client",
"summary": "Send passive alerts to monitor your Odoo application.", "summary": "Send passive alerts to monitor your Odoo application.",
"version": "8.0.1.0.0",
"version": "11.0.1.0.0",
"category": "Tools", "category": "Tools",
"website": "http://osiell.com/",
"website": "http://github.com/OCA/server-tools",
"author": "ABF OSIELL, Odoo Community Association (OCA)", "author": "ABF OSIELL, Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"application": False, "application": False,
"installable": True, "installable": True,
"data": [ "data": [
"security/ir.model.access.csv", "security/ir.model.access.csv",
"data/nsca_server.xml",
"views/nsca_menu.xml", "views/nsca_menu.xml",
"views/nsca_check.xml", "views/nsca_check.xml",
"views/nsca_server.xml", "views/nsca_server.xml",

14
nsca_client/data/nsca_server.xml

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 ABF OSIELL <http://osiell.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data noupdate="1">
<record id="nsca_server_default" model="nsca.server">
<field name="name">nagios.example.net</field>
<field name="port">5667</field>
<field name="node_hostname">MY-SERVER</field>
</record>
</data>
</openerp>

12
nsca_client/demo/demo_data.xml

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<data noupdate="1">
<record id="nsca_server_default" model="nsca.server">
<field name="name">nagios.example.net</field>
<field name="port">5667</field>
<field name="node_hostname">MY-SERVER</field>
</record>
<record id="demo_nsca_check_mails" model="nsca.check"> <record id="demo_nsca_check_mails" model="nsca.check">
<field name="server_id" ref="nsca_server_default"/> <field name="server_id" ref="nsca_server_default"/>
@ -13,4 +19,4 @@
</record> </record>
</data> </data>
</openerp>
</odoo>

4
nsca_client/models/__init__.py

@ -1,5 +1 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import nsca_check, nsca_server from . import nsca_check, nsca_server

64
nsca_client/models/nsca_check.py

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com>
# (Copyright) 2015 ABF OSIELL <http://osiell.com>
# (Copyright) 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging import logging
@ -7,10 +7,9 @@ import os
import shlex import shlex
import subprocess 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__) _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). has the side effect to re-create the fields on the current model).
""" """
res = super(NscaCheck, self).default_get(fields_list) 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_number'] = 10
res['interval_type'] = 'minutes' res['interval_type'] = 'minutes'
res['server_id'] = NscaServer.search([])[0].id
return res return res
@api.multi @api.multi
@ -59,12 +56,13 @@ class NscaCheck(models.Model):
- Compute the name of the NSCA check to be readable - Compute the name of the NSCA check to be readable
among the others 'ir.cron' records. among the others 'ir.cron' records.
""" """
model = self.env['ir.model'].search([('model', '=', self._name)])
for check in self: for check in self:
vals = { vals = {
'name': u"%s - %s" % (_(u"NSCA Check"), check.service), '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, 'doall': False,
'numbercall': -1 'numbercall': -1
} }
@ -72,6 +70,11 @@ class NscaCheck(models.Model):
@api.model @api.model
def create(self, vals): 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 = super(NscaCheck, self).create(vals)
check._force_values() check._force_values()
return check return check
@ -87,28 +90,47 @@ class NscaCheck(models.Model):
def _cron_check(self, check_id): def _cron_check(self, check_id):
self._check_send_nsca_command() self._check_send_nsca_command()
check = self.browse(check_id) check = self.browse(check_id)
rc, message = 3, "Unknown"
rc, message, performance = 3, "Unknown", {}
try: try:
args = str2tuple(check.nsca_args)
NscaModel = self.env[check.nsca_model] 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 not result:
if check.allow_void_result: if check.allow_void_result:
return False return False
raise ValueError( raise ValueError(
"'%s' method does not return" % check.nsca_function) "'%s' method does not return" % check.nsca_function)
if len(result) == 2:
rc, message = result rc, message = result
except Exception, exc:
else:
rc, message, performance = result
except Exception as exc:
rc, message = 2, "%s" % 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 return True
@api.multi @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.""" """Send the result of the check to the NSCA daemon."""
for check in self: 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) cmd = self._prepare_command(check)
self._run_command(check, cmd, check_result) self._run_command(check, cmd, check_result)
@ -143,8 +165,8 @@ class NscaCheck(models.Model):
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
stdout = proc.communicate( stdout = proc.communicate(
input=check_result)[0] 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) _logger.error(exc)
def _check_send_nsca_command(self): def _check_send_nsca_command(self):

50
nsca_client/models/nsca_server.py

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com>
# (Copyright) 2015 ABF OSIELL <http://osiell.com>
# (Copyright) 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import psutil
import os 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): class NscaServer(models.Model):
@ -99,3 +100,44 @@ class NscaServer(models.Model):
res = super(NscaServer, self).create(vals) res = super(NscaServer, self).create(vals)
res.write_config_file() res.write_config_file()
return res 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

1
nsca_client/tests/__init__.py

@ -0,0 +1 @@
from . import test_nsca

97
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()

33
nsca_client/views/nsca_check.xml

@ -2,7 +2,7 @@
<!-- © 2015 ABF OSIELL <http://osiell.com> <!-- © 2015 ABF OSIELL <http://osiell.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<odoo>
<data> <data>
<record id="view_nsca_check_form" model="ir.ui.view"> <record id="view_nsca_check_form" model="ir.ui.view">
@ -11,14 +11,17 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="NSCA Check"> <form string="NSCA Check">
<sheet> <sheet>
<group col="4">
<group>
<group string="NSCA Check"> <group string="NSCA Check">
<field name="server_id"/> <field name="server_id"/>
<field name="service"/> <field name="service"/>
<label for="interval_number" string="Frequency"/>
<label for="interval_number"
string="Frequency"/>
<div> <div>
<field name="interval_number" class="oe_inline"/>
<field name="interval_type" class="oe_inline"/>
<field name="interval_number"
class="oe_inline"/>
<field name="interval_type"
class="oe_inline"/>
</div> </div>
<field name="nextcall"/> <field name="nextcall"/>
<field name="active"/> <field name="active"/>
@ -26,10 +29,12 @@
<group string="Settings"> <group string="Settings">
<field name="nsca_model"/> <field name="nsca_model"/>
<field name="nsca_function"/> <field name="nsca_function"/>
<field name="nsca_args"/>
<field name="nsca_args" widget="ace" options="{'mode': 'python'}" />
<field name="allow_void_result"/> <field name="allow_void_result"/>
<div colspan="2"> <div colspan="2">
<p>The method must return a tuple (RC, MESSAGE) where RC is an integer:</p>
<p>The method must return a tuple (RC,
MESSAGE) where RC is an integer:
</p>
<newline/> <newline/>
<ul> <ul>
<li>0: OK</li> <li>0: OK</li>
@ -38,8 +43,12 @@
<li>3: UNKNOWN</li> <li>3: UNKNOWN</li>
</ul> </ul>
<newline/> <newline/>
<p>Any other RC value will be treated as CRITICAL.</p>
<p>E.g. <em>(1, u"3 mails not sent")</em></p>
<p>Any other RC value will be treated as
CRITICAL.
</p>
<p>E.g.
<em>(1, u"3 mails not sent")</em>
</p>
</div> </div>
</group> </group>
</group> </group>
@ -68,7 +77,9 @@
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_id" ref="view_nsca_check_tree"/> <field name="view_id" ref="view_nsca_check_tree"/>
<field name="context" eval="{'default_active': True}"/> <field name="context" eval="{'default_active': True}"/>
<field name="domain">['|', ('active', '=', True), ('active', '=', False)]</field>
<field name="domain">['|', ('active', '=', True), ('active', '=',
False)]
</field>
</record> </record>
<menuitem id="menu_action_nsca_check_tree" <menuitem id="menu_action_nsca_check_tree"
@ -76,4 +87,4 @@
action="action_nsca_check_tree"/> action="action_nsca_check_tree"/>
</data> </data>
</openerp>
</odoo>

4
nsca_client/views/nsca_menu.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 ABF OSIELL <http://osiell.com> <!-- © 2015 ABF OSIELL <http://osiell.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<odoo>
<data> <data>
<menuitem id="menu_nsca_client" <menuitem id="menu_nsca_client"
@ -9,4 +9,4 @@
name="NSCA Client"/> name="NSCA Client"/>
</data> </data>
</openerp>
</odoo>

4
nsca_client/views/nsca_server.xml

@ -2,7 +2,7 @@
<!-- © 2015 ABF OSIELL <http://osiell.com> <!-- © 2015 ABF OSIELL <http://osiell.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<odoo>
<data> <data>
<record id="view_nsca_server_form" model="ir.ui.view"> <record id="view_nsca_server_form" model="ir.ui.view">
@ -54,4 +54,4 @@
action="action_nsca_server_tree"/> action="action_nsca_server_tree"/>
</data> </data>
</openerp>
</odoo>
Loading…
Cancel
Save