Browse Source

[MIG] nsca_client

pull/1280/head
Enric Tobella 6 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. 32
      nsca_client/demo/demo_data.xml
  7. 4
      nsca_client/models/__init__.py
  8. 68
      nsca_client/models/nsca_check.py
  9. 52
      nsca_client/models/nsca_server.py
  10. 1
      nsca_client/tests/__init__.py
  11. 97
      nsca_client/tests/test_nsca.py
  12. 151
      nsca_client/views/nsca_check.xml
  13. 4
      nsca_client/views/nsca_menu.xml
  14. 94
      nsca_client/views/nsca_server.xml

1
.travis.yml

@ -12,6 +12,7 @@ addons:
packages:
- expect-dev # provides unbuffer utility
- python-lxml # because pip installation is slow
- nsca-client
env:
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.
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
<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,
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
=======
@ -98,6 +108,7 @@ Contributors
------------
* Sébastien Alix <sebastien.alix@osiell.com>
* Enric Tobella <etobella@creublanca.es>
Maintainer
----------

1
nsca_client/__init__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com>
# 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>
# 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",

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>

32
nsca_client/demo/demo_data.xml

@ -1,16 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<data noupdate="1">
<record id="demo_nsca_check_mails" model="nsca.check">
<field name="server_id" ref="nsca_server_default"/>
<field name="service">Odoo Mail Queue</field>
<field name="interval_number" eval="10"/>
<field name="interval_type">minutes</field>
<field name="nsca_model">mail.mail</field>
<field name="nsca_function">nsca_check_mails</field>
<field name="nsca_args"></field>
</record>
<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>
<record id="demo_nsca_check_mails" model="nsca.check">
<field name="server_id" ref="nsca_server_default"/>
<field name="service">Odoo Mail Queue</field>
<field name="interval_number" eval="10"/>
<field name="interval_type">minutes</field>
<field name="nsca_model">mail.mail</field>
<field name="nsca_function">nsca_check_mails</field>
<field name="nsca_args"></field>
</record>
</data>
</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

68
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).
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):

52
nsca_client/models/nsca_server.py

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# © 2015 ABF OSIELL <http://osiell.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# (Copyright) 2015 ABF OSIELL <http://osiell.com>
# (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

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

151
nsca_client/views/nsca_check.xml

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

4
nsca_client/views/nsca_menu.xml

@ -1,7 +1,7 @@
<?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>
<odoo>
<data>
<menuitem id="menu_nsca_client"
@ -9,4 +9,4 @@
name="NSCA Client"/>
</data>
</openerp>
</odoo>

94
nsca_client/views/nsca_server.xml

@ -2,56 +2,56 @@
<!-- © 2015 ABF OSIELL <http://osiell.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<odoo>
<data>
<record id="view_nsca_server_form" model="ir.ui.view">
<field name="name">nsca.server.form</field>
<field name="model">nsca.server</field>
<field name="arch" type="xml">
<form string="NSCA Server">
<sheet>
<group string="NSCA Server">
<field name="name" style="width: 30%;"/>
<record id="view_nsca_server_form" model="ir.ui.view">
<field name="name">nsca.server.form</field>
<field name="model">nsca.server</field>
<field name="arch" type="xml">
<form string="NSCA Server">
<sheet>
<group string="NSCA Server">
<field name="name" style="width: 30%;"/>
<field name="port"/>
<field name="password" password="1"/>
<field name="encryption_method"/>
<field name="config_file_path"/>
</group>
<group string="Node identity">
<field name="node_hostname"/>
</group>
<group string="Checks">
<field name="check_ids" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_nsca_server_tree" model="ir.ui.view">
<field name="name">nsca.server.tree</field>
<field name="model">nsca.server</field>
<field name="arch" type="xml">
<tree string="NSCA Servers">
<field name="name"/>
<field name="port"/>
<field name="password" password="1"/>
<field name="encryption_method"/>
<field name="config_file_path"/>
</group>
<group string="Node identity">
<field name="node_hostname"/>
</group>
<group string="Checks">
<field name="check_ids" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_nsca_server_tree" model="ir.ui.view">
<field name="name">nsca.server.tree</field>
<field name="model">nsca.server</field>
<field name="arch" type="xml">
<tree string="NSCA Servers">
<field name="name"/>
<field name="port"/>
<field name="config_file_path"/>
</tree>
</field>
</record>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="action_nsca_server_tree">
<field name="name">Servers</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">nsca.server</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_nsca_server_tree"/>
</record>
<record model="ir.actions.act_window" id="action_nsca_server_tree">
<field name="name">Servers</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">nsca.server</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_nsca_server_tree"/>
</record>
<menuitem id="menu_action_nsca_server_tree"
parent="menu_nsca_client"
action="action_nsca_server_tree"/>
<menuitem id="menu_action_nsca_server_tree"
parent="menu_nsca_client"
action="action_nsca_server_tree"/>
</data>
</openerp>
</data>
</odoo>
Loading…
Cancel
Save