You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
6.0 KiB

9 years ago
9 years ago
  1. # -*- coding: utf-8 -*-
  2. # © 2015 Therp BV <http://therp.nl>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import json
  5. from datetime import datetime, timedelta
  6. from openerp import _, api, fields, models
  7. class DeadMansSwitchInstance(models.Model):
  8. _inherit = ['mail.thread', 'ir.needaction_mixin']
  9. _name = 'dead.mans.switch.instance'
  10. _description = 'Instance to monitor'
  11. _order = 'state, partner_id'
  12. _rec_name = 'partner_id'
  13. state = fields.Selection(
  14. [('new', 'New'), ('active', 'Active'), ('suspended', 'Suspended')],
  15. 'State', default='new')
  16. partner_id = fields.Many2one(
  17. 'res.partner', 'Customer',
  18. domain=[('is_company', '=', True), ('customer', '=', True)])
  19. database_uuid = fields.Char('Database id', required=True, readonly=True)
  20. user_id = fields.Many2one('res.users', 'Responsible user',
  21. track_visibility='onchange')
  22. description = fields.Char('Description')
  23. log_ids = fields.One2many(
  24. 'dead.mans.switch.log', 'instance_id', string='Log lines')
  25. alive = fields.Boolean(
  26. 'Alive', compute='_compute_alive', search='_search_alive')
  27. alive_max_delay = fields.Integer(
  28. 'Alive delay', help='The amount of seconds without notice after which '
  29. 'the instance is considered dead', default=600)
  30. last_seen = fields.Datetime('Last seen', compute='_compute_last_log')
  31. last_cpu = fields.Float('CPU', compute='_compute_last_log')
  32. last_cpu_sparkline = fields.Text('CPU', compute='_compute_last_log')
  33. last_ram = fields.Float('RAM', compute='_compute_last_log')
  34. last_ram_sparkline = fields.Text('RAM', compute='_compute_last_log')
  35. last_user_count = fields.Integer(
  36. 'Active users', compute='_compute_last_log')
  37. last_user_count_sparkline = fields.Text(
  38. 'Active users', compute='_compute_last_log')
  39. _sql_constraints = [
  40. ('uuid_unique', 'unique(database_uuid)', 'Database ID must be unique'),
  41. ]
  42. @api.multi
  43. def name_get(self):
  44. return [
  45. (
  46. this.id,
  47. '%s%s' % (
  48. this.partner_id.name or this.database_uuid,
  49. ' (%s)' % (this.description) if this.description else '',
  50. )
  51. )
  52. for this in self
  53. ]
  54. @api.onchange('partner_id')
  55. def _onchange_partner_id(self):
  56. if not self.user_id:
  57. self.user_id = self.partner_id.user_id
  58. @api.multi
  59. def button_active(self):
  60. self.write({'state': 'active'})
  61. @api.multi
  62. def button_suspended(self):
  63. self.write({'state': 'suspended'})
  64. @api.multi
  65. def button_logs(self):
  66. return {
  67. 'type': 'ir.actions.act_window',
  68. 'res_model': 'dead.mans.switch.log',
  69. 'domain': [('instance_id', 'in', self.ids)],
  70. 'name': _('Logs'),
  71. 'view_mode': 'graph,tree,form',
  72. 'context': {
  73. 'search_default_this_month': 1,
  74. },
  75. }
  76. @api.multi
  77. def _compute_alive(self):
  78. for this in self:
  79. if this.state in ['new', 'suspended']:
  80. this.alive = False
  81. continue
  82. this.alive = bool(
  83. self.env['dead.mans.switch.log'].search(
  84. [
  85. ('instance_id', '=', this.id),
  86. ('create_date', '>=', fields.Datetime.to_string(
  87. datetime.utcnow() -
  88. timedelta(seconds=this.alive_max_delay))),
  89. ],
  90. limit=1))
  91. @api.model
  92. def _search_alive(self, operator, value):
  93. alive = True if operator == '=' and value or\
  94. operator == '!=' and not value else False
  95. self.env.cr.execute(
  96. 'select i.id from dead_mans_switch_instance i '
  97. 'left join (select instance_id, max(create_date) create_date '
  98. 'from dead_mans_switch_log group by instance_id) l '
  99. 'on l.instance_id=i.id '
  100. "where coalesce(l.create_date, '1970-01-01'::timestamp) %s "
  101. "now() at time zone 'utc' - "
  102. "(2 * alive_max_delay || 'seconds')::interval "
  103. "group by i.id " %
  104. (alive and '>=' or '<'))
  105. return [('id', 'in', [i for i, in self.env.cr.fetchall()])]
  106. @api.multi
  107. def _compute_last_log(self):
  108. for this in self:
  109. last_log = self.env['dead.mans.switch.log'].search(
  110. [('instance_id', '=', this.id)], limit=12)
  111. field_mapping = {
  112. 'last_seen': 'create_date',
  113. 'last_cpu': 'cpu',
  114. 'last_ram': 'ram',
  115. 'last_user_count': 'user_count',
  116. }
  117. for field, mapped_field in field_mapping.iteritems():
  118. this[field] = last_log[:1][mapped_field]
  119. sparkline_fields = ['last_cpu', 'last_ram', 'last_user_count']
  120. for field in sparkline_fields:
  121. this['%s_sparkline' % field] = json.dumps(
  122. list(reversed(last_log.mapped(lambda log: {
  123. 'value': log[field_mapping[field]],
  124. 'tooltip': log.create_date,
  125. }))))
  126. @api.model
  127. def check_alive(self):
  128. """handle cronjob"""
  129. for this in self.search(self._needaction_domain_get()):
  130. this.panic()
  131. @api.multi
  132. def panic(self):
  133. """override for custom handling"""
  134. self.ensure_one()
  135. last_post = fields.Datetime.from_string(self.message_last_post)
  136. if last_post and last_post >= datetime.utcnow() - 3 * timedelta(
  137. seconds=self.alive_max_delay):
  138. # don't nag too often
  139. return
  140. self.message_post(
  141. type='comment', subtype='mt_comment',
  142. subject=_('Dead man\'s switch warning: %s') %
  143. self.display_name, content_subtype='plaintext',
  144. body=_('%s seems to be dead') % self.display_name)
  145. @api.model
  146. def _needaction_domain_get(self):
  147. return [('alive', '=', False), ('state', '=', 'active')]