Initial commit
This commit is contained in:
commit
7550e63940
19 changed files with 1654 additions and 0 deletions
161
rattail_tempmon/server.py
Normal file
161
rattail_tempmon/server.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2016 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Tempmon server daemon
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import time
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from rattail.db import Session, api
|
||||
from rattail_tempmon import Session as TempmonSession
|
||||
from rattail.daemon import Daemon
|
||||
from rattail.time import localtime, make_utc
|
||||
from rattail.mail import send_email
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TempmonServerDaemon(Daemon):
|
||||
"""
|
||||
Linux daemon implementation of tempmon server.
|
||||
"""
|
||||
timefmt = '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Keeps an eye on tempmon readings and sends alerts as needed.
|
||||
"""
|
||||
self.extra_emails = self.config.getlist('rattail.tempmon', 'extra_emails', default=[])
|
||||
while True:
|
||||
self.check_readings()
|
||||
time.sleep(5)
|
||||
|
||||
def check_readings(self):
|
||||
self.now = make_utc()
|
||||
session = TempmonSession()
|
||||
probes = session.query(tempmon.Probe)\
|
||||
.join(tempmon.Client)\
|
||||
.filter(tempmon.Client.enabled == True)\
|
||||
.filter(tempmon.Probe.enabled == True)\
|
||||
.all()
|
||||
|
||||
if probes:
|
||||
cutoff = self.now - datetime.timedelta(seconds=120)
|
||||
uuids = [probe.uuid for probe in probes]
|
||||
readings = session.query(tempmon.Reading)\
|
||||
.filter(tempmon.Reading.probe_uuid.in_(uuids))\
|
||||
.filter(tempmon.Reading.taken >= cutoff)\
|
||||
.all()
|
||||
self.process_readings(probes, readings)
|
||||
|
||||
else:
|
||||
log.warning("found no enabled probes!")
|
||||
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
# TODO: not sure this is really necessary?
|
||||
self.set_last_checked()
|
||||
|
||||
def process_readings(self, probes, readings):
|
||||
for probe in probes:
|
||||
probe_readings = [r for r in readings if r.probe is probe]
|
||||
if probe_readings:
|
||||
reading = sorted(probe_readings, key=lambda r: r.taken)[-1]
|
||||
|
||||
if (reading.degrees_f <= probe.critical_temp_min or
|
||||
reading.degrees_f >= probe.critical_temp_max):
|
||||
self.update_status(probe, self.enum.TEMPMON_PROBE_STATUS_CRITICAL_TEMP, reading)
|
||||
|
||||
elif reading.degrees_f < probe.good_temp_min:
|
||||
self.update_status(probe, self.enum.TEMPMON_PROBE_STATUS_LOW_TEMP, reading)
|
||||
|
||||
elif reading.degrees_f > probe.good_temp_max:
|
||||
self.update_status(probe, self.enum.TEMPMON_PROBE_STATUS_HIGH_TEMP, reading)
|
||||
|
||||
else: # temp is good
|
||||
self.update_status(probe, self.enum.TEMPMON_PROBE_STATUS_GOOD_TEMP, reading)
|
||||
|
||||
else: # no readings for probe
|
||||
self.update_status(probe, self.enum.TEMPMON_PROBE_STATUS_ERROR)
|
||||
|
||||
def update_status(self, probe, status, reading=None):
|
||||
prev_status = probe.status
|
||||
if probe.status != status:
|
||||
probe.status = status
|
||||
probe.status_changed = self.now
|
||||
probe.status_alert_sent = None
|
||||
|
||||
# no email if status is good
|
||||
if status == self.enum.TEMPMON_PROBE_STATUS_GOOD_TEMP:
|
||||
return
|
||||
|
||||
# no email if we already sent one...until timeout is reached
|
||||
if probe.status_alert_sent:
|
||||
timeout = datetime.timedelta(minutes=probe.status_alert_timeout)
|
||||
if (self.now - probe.status_alert_sent) <= timeout:
|
||||
return
|
||||
|
||||
msgtypes = {
|
||||
self.enum.TEMPMON_PROBE_STATUS_LOW_TEMP : 'tempmon_low_temp',
|
||||
self.enum.TEMPMON_PROBE_STATUS_HIGH_TEMP : 'tempmon_high_temp',
|
||||
self.enum.TEMPMON_PROBE_STATUS_CRITICAL_TEMP : 'tempmon_critical_temp',
|
||||
self.enum.TEMPMON_PROBE_STATUS_ERROR : 'tempmon_error',
|
||||
}
|
||||
|
||||
data = {
|
||||
'probe': probe,
|
||||
'status': self.enum.TEMPMON_PROBE_STATUS[status],
|
||||
'reading': reading,
|
||||
'taken': localtime(self.config, make_utc(reading.taken, tzinfo=True)) if reading else None,
|
||||
'now': localtime(self.config),
|
||||
}
|
||||
|
||||
send_email(self.config, msgtypes[status], data)
|
||||
|
||||
# maybe send more emails if config said so
|
||||
for msgtype in self.extra_emails:
|
||||
send_email(self.config, msgtype, data)
|
||||
|
||||
probe.status_alert_sent = self.now
|
||||
|
||||
def set_last_checked(self):
|
||||
session = Session()
|
||||
api.save_setting(session, 'tempmon.server.last_checked', self.now.strftime(self.timefmt))
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
|
||||
def make_daemon(config, pidfile=None):
|
||||
"""
|
||||
Returns a tempmon server daemon instance.
|
||||
"""
|
||||
if not pidfile:
|
||||
pidfile = config.get('rattail.tempmon', 'server.pid_path',
|
||||
default='/var/run/rattail/tempmon-server.pid')
|
||||
return TempmonServerDaemon(pidfile, config=config)
|
Loading…
Add table
Add a link
Reference in a new issue