From aad4bd2ffb83035e6ab90bb69935b51c2862953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Tue, 16 Jun 2015 11:33:12 +0300 Subject: [PATCH] Background worker to send notifications --- hc/api/admin.py | 1 + hc/api/management/commands/ensuretriggers.py | 27 ++++++++++++ hc/api/management/commands/sendalerts.py | 45 ++++++++++++++++++++ hc/api/migrations/0002_auto_20150616_0732.py | 35 +++++++++++++++ hc/api/models.py | 21 +++++++++ hc/api/views.py | 3 ++ hc/lib/emails.py | 15 +++++++ hc/settings.py | 2 +- 8 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 hc/api/management/commands/ensuretriggers.py create mode 100644 hc/api/management/commands/sendalerts.py create mode 100644 hc/api/migrations/0002_auto_20150616_0732.py create mode 100644 hc/lib/emails.py diff --git a/hc/api/admin.py b/hc/api/admin.py index 8a60577b..c3fc7e73 100644 --- a/hc/api/admin.py +++ b/hc/api/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin from hc.api.models import Check + @admin.register(Check) class ChecksAdmin(admin.ModelAdmin): list_display = ("id", "code", "user", "last_ping") diff --git a/hc/api/management/commands/ensuretriggers.py b/hc/api/management/commands/ensuretriggers.py new file mode 100644 index 00000000..7d8f97cc --- /dev/null +++ b/hc/api/management/commands/ensuretriggers.py @@ -0,0 +1,27 @@ +from django.core.management.base import BaseCommand +from django.db import connection + + +class Command(BaseCommand): + help = 'Ensures triggers exist in database' + + def handle(self, *args, **options): + cursor = connection.cursor() + + cursor.execute(""" +CREATE OR REPLACE FUNCTION update_alert_after() +RETURNS trigger AS $update_alert_after$ + BEGIN + IF NEW.last_ping IS NOT NULL THEN + NEW.alert_after := NEW.last_ping + NEW.timeout; + END IF; + RETURN NEW; + END; +$update_alert_after$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS update_alert_after ON api_check; + +CREATE TRIGGER update_alert_after +BEFORE INSERT OR UPDATE OF last_ping, timeout ON api_check +FOR EACH ROW EXECUTE PROCEDURE update_alert_after(); + """) diff --git a/hc/api/management/commands/sendalerts.py b/hc/api/management/commands/sendalerts.py new file mode 100644 index 00000000..f17e0b22 --- /dev/null +++ b/hc/api/management/commands/sendalerts.py @@ -0,0 +1,45 @@ +import sys +import time + +from django.core.management.base import BaseCommand +from django.utils import timezone + +from hc.api.models import Check +from hc.lib.emails import send_status_notification + + +def _log(message): + sys.stdout.write(message) + sys.stdout.flush() + + +class Command(BaseCommand): + help = 'Ensures triggers exist in database' + + def handle(self, *args, **options): + + while True: + # Gone down? + query = Check.objects + query = query.filter(alert_after__lt=timezone.now()) + query = query.filter(enabled=True, status="up") + for check in query: + check.status = "down" + check.save() + + _log("\nSending email about going down for %s\n" % check.code) + send_status_notification(check) + + # Gone up? + query = Check.objects + query = query.filter(alert_after__gt=timezone.now()) + query = query.filter(enabled=True, status="down") + for check in query: + check.status = "up" + check.save() + + _log("\nSending email about going up for %s\n" % check.code) + send_status_notification(check) + + time.sleep(1) + _log(".") diff --git a/hc/api/migrations/0002_auto_20150616_0732.py b/hc/api/migrations/0002_auto_20150616_0732.py new file mode 100644 index 00000000..2b3881e5 --- /dev/null +++ b/hc/api/migrations/0002_auto_20150616_0732.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='check', + name='alert_after', + field=models.DateTimeField(null=True, blank=True), + ), + migrations.AddField( + model_name='check', + name='enabled', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='check', + name='status', + field=models.CharField(max_length=6, choices=[('up', 'Up'), ('down', 'Down'), ('new', 'New')], default='new'), + ), + migrations.AddField( + model_name='check', + name='timeout', + field=models.DurationField(choices=[(datetime.timedelta(0, 300), '5 minutes'), (datetime.timedelta(0, 600), '10 minutes'), (datetime.timedelta(0, 1800), '30 minutes'), (datetime.timedelta(0, 3600), '1 hour'), (datetime.timedelta(0, 7200), '2 hours'), (datetime.timedelta(0, 21600), '6 hours'), (datetime.timedelta(0, 43200), '12 hours'), (datetime.timedelta(1), '1 day'), (datetime.timedelta(2), '2 days'), (datetime.timedelta(7), '1 week'), (datetime.timedelta(14), '2 weeks')], default=datetime.timedelta(1)), + ), + ] diff --git a/hc/api/models.py b/hc/api/models.py index 43e43474..544340b8 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -1,10 +1,31 @@ +from datetime import timedelta as td import uuid from django.contrib.auth.models import User from django.db import models +STATUSES = (("up", "Up"), ("down", "Down"), ("new", "New")) +ONEDAY = td(days=1) +DURATIONS = ( + (td(minutes=5), "5 minutes"), + (td(minutes=10), "10 minutes"), + (td(minutes=30), "30 minutes"), + (td(hours=1), "1 hour"), + (td(hours=2), "2 hours"), + (td(hours=6), "6 hours"), + (td(hours=12), "12 hours"), + (ONEDAY, "1 day"), + (td(days=2), "2 days"), + (td(weeks=1), "1 week"), + (td(weeks=2), "2 weeks") +) + class Check(models.Model): code = models.UUIDField(default=uuid.uuid4, editable=False) user = models.ForeignKey(User) + enabled = models.BooleanField(default=True) + timeout = models.DurationField(default=ONEDAY) last_ping = models.DateTimeField(null=True, blank=True) + alert_after = models.DateTimeField(null=True, blank=True, editable=False) + status = models.CharField(max_length=6, choices=STATUSES, default="new") diff --git a/hc/api/views.py b/hc/api/views.py index ee527ada..23c20361 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -11,6 +11,9 @@ def ping(request, code): return HttpResponseBadRequest() check.last_ping = timezone.now() + if check.status == "new": + check.status = "up" + check.save() return HttpResponse() diff --git a/hc/lib/emails.py b/hc/lib/emails.py new file mode 100644 index 00000000..ecd2fbcf --- /dev/null +++ b/hc/lib/emails.py @@ -0,0 +1,15 @@ +from django.core.mail import send_mail + + +def send_status_notification(check): + if check.status == "down": + subject = "Alert DOWN" + body = "Hi, the check %s has gone down" % check.code + elif check.status == "up": + subject = "Alert UP" + body = "Hi, the check %s has gone up" % check.code + else: + raise NotImplemented("Unexpected status: %s" % check.status) + + send_mail(subject, body, 'cuu508@gmail.com', [check.user.email], + fail_silently=False) diff --git a/hc/settings.py b/hc/settings.py index 0b0a451c..e1b90e42 100644 --- a/hc/settings.py +++ b/hc/settings.py @@ -85,7 +85,7 @@ DATABASES = { 'NAME': 'hc', 'USER': 'hc', 'PASSWORD': '', - 'HOST': '192.168.1.111', + 'HOST': '192.168.1.112', 'PORT': 5432, 'TEST': {'CHARSET': 'UTF8'} }