diff --git a/dead_mans_switch_client/__init__.py b/dead_mans_switch_client/__init__.py new file mode 100644 index 000000000..915f58d4d --- /dev/null +++ b/dead_mans_switch_client/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# © 2015 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/dead_mans_switch_client/__openerp__.py b/dead_mans_switch_client/__openerp__.py new file mode 100644 index 000000000..ec656035a --- /dev/null +++ b/dead_mans_switch_client/__openerp__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# © 2015 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Dead man's switch (client)", + "version": "1.0.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Monitoring", + "description": """ +========================== +Dead man's switch (client) +========================== + +This module is the client part of `dead_mans_switch_server`. It is responsible +of sending the server status updates, which in turn takes action if those +updates don't come in time. + +Configuration +============= + +After installing this module, you need to fill in the system parameter +`dead_mans_switch_client.url`. This must be the full URL to the server's +controller, usually of the form https://your.server/dead_mans_switch/alive + +This module attempts to send CPU and RAM statistics to the server. While this +is not mandatory, it's helpful for assessing a server's health. If you want +this, you need to install `psutil`.""", + "depends": [ + 'base', + ], + "data": [ + "data/ir_actions.xml", + "data/ir_cron.xml", + ], +} diff --git a/dead_mans_switch_client/data/ir_actions.xml b/dead_mans_switch_client/data/ir_actions.xml new file mode 100644 index 000000000..5728882a5 --- /dev/null +++ b/dead_mans_switch_client/data/ir_actions.xml @@ -0,0 +1,13 @@ + + + + + + Configure the dead man's switch server + automatic + + + + diff --git a/dead_mans_switch_client/data/ir_cron.xml b/dead_mans_switch_client/data/ir_cron.xml new file mode 100644 index 000000000..fe786326d --- /dev/null +++ b/dead_mans_switch_client/data/ir_cron.xml @@ -0,0 +1,13 @@ + + + + + Dead man's switch client + 5 + minutes + -1 + dead.mans.switch.client + alive + + + diff --git a/dead_mans_switch_client/models/__init__.py b/dead_mans_switch_client/models/__init__.py new file mode 100644 index 000000000..a1f543c73 --- /dev/null +++ b/dead_mans_switch_client/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# © 2015 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import dead_mans_switch_client diff --git a/dead_mans_switch_client/models/dead_mans_switch_client.py b/dead_mans_switch_client/models/dead_mans_switch_client.py new file mode 100644 index 000000000..9fe2719a9 --- /dev/null +++ b/dead_mans_switch_client/models/dead_mans_switch_client.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# © 2015 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import json +import logging +import os +try: + import psutil +except ImportError: + psutil = None +import urllib2 +from openerp.osv import orm +from openerp.tools.config import config + + +class DeadMansSwitchClient(orm.AbstractModel): + _name = 'dead.mans.switch.client' + _register = True + + def _get_data(self, cr, uid, context=None): + 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: + 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() + elif hasattr(process, 'get_memory_percent'): + ram += process.get_memory_percent() + else: + ram = None + if hasattr(process, 'cpu_percent'): + cpu += process.cpu_percent() + elif hasattr(process, 'get_cpu_percent'): + cpu += process.get_cpu_percent() + else: + cpu = None + return { + 'database_uuid': self.pool['ir.config_parameter'].get_param( + cr, uid, 'database.uuid', context=context), + 'cpu': cpu, + 'ram': ram, + 'user_count': None, + } + + def alive(self, cr, uid, context=None): + url = self.pool['ir.config_parameter'].get_param( + cr, uid, 'dead_mans_switch_client.url') + logger = logging.getLogger(__name__) + if not url: + logger.error('No server configured!') + return + data = self._get_data(cr, uid, context=context) + logger.debug('sending %s', data) + urllib2.urlopen( + urllib2.Request( + url, + json.dumps({ + 'jsonrpc': '2.0', + 'method': 'call', + 'params': data, + }), + { + 'Content-Type': 'application/json', + })) diff --git a/dead_mans_switch_client/static/src/img/icon.png b/dead_mans_switch_client/static/src/img/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/dead_mans_switch_client/static/src/img/icon.png differ diff --git a/dead_mans_switch_client/tests/__init__.py b/dead_mans_switch_client/tests/__init__.py new file mode 100644 index 000000000..3c894c3c9 --- /dev/null +++ b/dead_mans_switch_client/tests/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# © 2015 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_dead_mans_switch_client + +fast_suite = [ + test_dead_mans_switch_client, +] diff --git a/dead_mans_switch_client/tests/test_dead_mans_switch_client.py b/dead_mans_switch_client/tests/test_dead_mans_switch_client.py new file mode 100644 index 000000000..4d81c7a51 --- /dev/null +++ b/dead_mans_switch_client/tests/test_dead_mans_switch_client.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# © 2015 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp.tests.common import TransactionCase + + +class TestDeadMansSwitchClient(TransactionCase): + def test_dead_mans_switch_client(self): + # test unconfigured case + ir_config_parameter = self.registry('ir.config_parameter') + ir_config_parameter.unlink( + self.cr, self.uid, + ir_config_parameter.search( + self.cr, self.uid, + [('key', '=', 'dead_mans_switch_client.url')])) + dead_mans_switch_client = self.registry('dead.mans.switch.client') + dead_mans_switch_client.alive(self.cr, self.uid) + # test configured case + ir_config_parameter.set_param( + self.cr, self.uid, 'dead_mans_switch_client.url', 'fake_url') + with self.assertRaises(ValueError): + dead_mans_switch_client.alive(self.cr, self.uid)