diff options
author | Brandon Lo <lobrandon1217@gmail.com> | 2019-11-26 16:39:26 -0500 |
---|---|---|
committer | Brandon Lo <lobrandon1217@gmail.com> | 2019-12-03 15:16:16 -0500 |
commit | 6be40a5f2d75b157cf2a2374f2f866f6fdc92b18 (patch) | |
tree | bdfa3b9ed77ebfef33f58262950ad551d579e3bb /src | |
parent | f1d7b9300fbb06495c6087f975cdbb68a894da37 (diff) |
Add warning email and notification
This adds the abandoned changes made to the notification
system and also adds a simple task to check for expiring
bookings and sends out emails and notifications.
Change-Id: I1530d19f41cf93626bb642e6b269f9ec55860b81
Signed-off-by: Brandon Lo <lobrandon1217@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/laas_dashboard/settings.py | 6 | ||||
-rw-r--r-- | src/notifier/admin.py | 3 | ||||
-rw-r--r-- | src/notifier/manager.py | 57 | ||||
-rw-r--r-- | src/notifier/migrations/0006_emailed.py | 24 | ||||
-rw-r--r-- | src/notifier/models.py | 27 | ||||
-rw-r--r-- | src/notifier/tasks.py | 35 | ||||
-rw-r--r-- | src/templates/notifier/email_ended.txt | 6 | ||||
-rw-r--r-- | src/templates/notifier/email_expiring.txt | 25 | ||||
-rw-r--r-- | src/templates/notifier/email_fulfilled.txt | 4 | ||||
-rw-r--r-- | src/templates/notifier/expiring_booking.html | 35 | ||||
-rw-r--r-- | src/templates/notifier/new_booking.html | 2 |
11 files changed, 215 insertions, 9 deletions
diff --git a/src/laas_dashboard/settings.py b/src/laas_dashboard/settings.py index b73e2c8..951ce1a 100644 --- a/src/laas_dashboard/settings.py +++ b/src/laas_dashboard/settings.py @@ -192,6 +192,10 @@ CELERYBEAT_SCHEDULE = { 'task': 'dashboard.tasks.free_hosts', 'schedule': timedelta(minutes=1) }, + 'notify_expiring': { + 'task': 'notifier.tasks.notify_expiring', + 'schedule': timedelta(hours=1) + }, } # Notifier Settings @@ -202,3 +206,5 @@ EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD'] EMAIL_USE_TLS = True DEFAULT_EMAIL_FROM = os.environ.get('DEFAULT_EMAIL_FROM', 'webmaster@localhost') SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" +EXPIRE_LIFETIME = 12 # Minimum lifetime of booking to send notification +EXPIRE_HOURS = 48 # Notify when booking is expiring within this many hours diff --git a/src/notifier/admin.py b/src/notifier/admin.py index 4a2984c..f6dbfd1 100644 --- a/src/notifier/admin.py +++ b/src/notifier/admin.py @@ -9,6 +9,7 @@ from django.contrib import admin -from notifier.models import Notification +from notifier.models import Notification, Emailed admin.site.register(Notification) +admin.site.register(Emailed) diff --git a/src/notifier/manager.py b/src/notifier/manager.py index 9b89ef5..ee849a8 100644 --- a/src/notifier/manager.py +++ b/src/notifier/manager.py @@ -7,10 +7,11 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## import os -from notifier.models import Notification +from notifier.models import Notification, Emailed from django.core.mail import send_mail from django.template.loader import render_to_string +from django.utils import timezone class NotificationHandler(object): @@ -28,6 +29,13 @@ class NotificationHandler(object): cls.booking_notify(booking, template, titles) @classmethod + def notify_booking_expiring(cls, booking): + template = "notifier/expiring_booking.html" + titles = ["Your booking (" + str(booking.id) + ") is about to expire", "A booking (" + str(booking.id) + ") that you collaborate on is about to expire"] + cls.booking_notify(booking, template, titles) + cls.email_booking_expiring(booking) + + @classmethod def booking_notify(cls, booking, template, titles): """ Creates a notification for a booking owner and collaborators @@ -72,7 +80,7 @@ class NotificationHandler(object): user_tasklist = [] # gather up all the relevant messages from the lab for task in all_tasks: - if (not hasattr(task, "user")) or task.user == user: + if (not hasattr(task.config, "user")) or task.config.user == user: user_tasklist.append( { "title": task.type_str() + " Message: ", @@ -81,6 +89,7 @@ class NotificationHandler(object): ) # gather up all the other needed info context = { + "owner": user == job.booking.owner, "user_name": user.userprofile.full_name, "messages": user_tasklist, "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(job.booking.id) + "/" @@ -118,7 +127,31 @@ class NotificationHandler(object): "Your Booking has Expired", message, os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@laas-dashboard"), - user.userprofile.email_addr, + [user.userprofile.email_addr], + fail_silently=False + ) + + @classmethod + def email_booking_expiring(cls, booking): + template_name = "notifier/email_expiring.txt" + hostnames = [host.template.resource.name for host in booking.resource.hosts.all()] + users = list(booking.collaborators.all()) + users.append(booking.owner) + for user in users: + context = { + "user_name": user.userprofile.full_name, + "booking": booking, + "hosts": hostnames, + "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(booking.id) + "/" + } + + message = render_to_string(template_name, context) + + send_mail( + "Your Booking is Expiring", + message, + os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@laas-dashboard"), + [user.userprofile.email_addr], fail_silently=False ) @@ -126,8 +159,20 @@ class NotificationHandler(object): def task_updated(cls, task): """ called every time a lab updated info about a task. - currently only checks if the job is now done so I can send an email, - may add more functionality later + sends an email when 'task' changing state means a booking has + just been fulfilled (all tasks done, servers ready to use) + or is over. """ + if task.job is None or task.job.booking is None: + return if task.job.is_fulfilled(): - cls.email_job_fulfilled(task.job) + if task.job.booking.end < timezone.now(): + if Emailed.objects.filter(end_booking=task.job.booking).exists(): + return + Emailed.objects.create(end_booking=task.job.booking) + cls.email_booking_over(task.job.booking) + if task.job.booking.end > timezone.now() and task.job.booking.start < timezone.now(): + if Emailed.objects.filter(begin_booking=task.job.booking).exists(): + return + Emailed.objects.create(begin_booking=task.job.booking) + cls.email_job_fulfilled(task.job) diff --git a/src/notifier/migrations/0006_emailed.py b/src/notifier/migrations/0006_emailed.py new file mode 100644 index 0000000..22ba9c5 --- /dev/null +++ b/src/notifier/migrations/0006_emailed.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2 on 2019-11-21 18:55 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('booking', '0006_booking_opnfv_config'), + ('notifier', '0005_auto_20190306_1616'), + ] + + operations = [ + migrations.CreateModel( + name='Emailed', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('almost_end_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='warning_mail', to='booking.Booking')), + ('begin_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='begin_mail', to='booking.Booking')), + ('end_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='over_mail', to='booking.Booking')), + ], + ), + ] diff --git a/src/notifier/models.py b/src/notifier/models.py index 49189e8..382d3a9 100644 --- a/src/notifier/models.py +++ b/src/notifier/models.py @@ -9,6 +9,7 @@ from django.db import models from account.models import UserProfile +from booking.models import Booking class Notification(models.Model): @@ -23,3 +24,29 @@ class Notification(models.Model): def to_preview_html(self): return "<h3>" + self.title + "</h3>" # TODO - template? + + +class Emailed(models.Model): + """ + A simple record to remember who has already gotten an email + to avoid resending + """ + begin_booking = models.OneToOneField( + Booking, + null=True, + on_delete=models.CASCADE, + related_name="begin_mail" + ) + almost_end_booking = models.OneToOneField( + Booking, + null=True, + on_delete=models.CASCADE, + related_name="warning_mail" + ) + end_booking = models.OneToOneField( + Booking, + null=True, + on_delete=models.CASCADE, + related_name="over_mail" + ) + diff --git a/src/notifier/tasks.py b/src/notifier/tasks.py new file mode 100644 index 0000000..b45ab8e --- /dev/null +++ b/src/notifier/tasks.py @@ -0,0 +1,35 @@ +############################################################################## +# Copyright (c) 2016 Max Breitenfeldt and others. +# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +from celery import shared_task +from django.utils import timezone +from django.conf import settings +from booking.models import Booking +from notifier.models import Emailed +from notifier.manager import NotificationHandler + + +@shared_task +def notify_expiring(): + """ + Notify users if their booking is within 48 hours of expiring. + """ + expire_time = timezone.now() + timezone.timedelta(hours=settings.EXPIRE_HOURS) + # Don't email people about bookings that have started recently + start_time = timezone.now() - timezone.timedelta(hours=settings.EXPIRE_LIFETIME) + bookings = Booking.objects.filter(end__lte=expire_time, + end__gte=timezone.now(), + start__lte=start_time) + for booking in bookings: + if Emailed.objects.filter(almost_end_booking=booking).exists(): + continue + NotificationHandler.notify_booking_expiring(booking) + Emailed.objects.create(almost_end_booking=booking) diff --git a/src/templates/notifier/email_ended.txt b/src/templates/notifier/email_ended.txt index 7467a0e..1788e00 100644 --- a/src/templates/notifier/email_ended.txt +++ b/src/templates/notifier/email_ended.txt @@ -1,6 +1,10 @@ {{user_name|default:"Developer"}}, -The booking you requested of the OPNFV Lab as a Service has ended. +{% if owner %} +The booking you requested from the OPNFV Lab as a Service has ended. +{% else %} +The booking you collaborated on at the OPNFV Lab as a Service has ended. +{% endif %} booking information: start: {{booking.start}} diff --git a/src/templates/notifier/email_expiring.txt b/src/templates/notifier/email_expiring.txt new file mode 100644 index 0000000..72c9a83 --- /dev/null +++ b/src/templates/notifier/email_expiring.txt @@ -0,0 +1,25 @@ +{{user_name|default:"Developer"}}, + +{% if owner %} +The booking you requested from the OPNFV Lab as a Service is about to expire. +{% else %} +The booking you collaborate on at the OPNFV Lab as a Service is about to expire. +{% endif %} + +booking information: + start: {{booking.start}} + end: {{booking.end}} + machines: + {% for host in hosts %} + - {{host}} + {% endfor %} + purpose: {{booking.purpose}} + +You may visit the following link for more information: +{{booking_url}} + +Please take the time to backup all data or extend the booking if needed. + +Thank you for contributing to the OPNFV platform! + + - The Lab-as-a-Service team diff --git a/src/templates/notifier/email_fulfilled.txt b/src/templates/notifier/email_fulfilled.txt index 65593db..90d294a 100644 --- a/src/templates/notifier/email_fulfilled.txt +++ b/src/templates/notifier/email_fulfilled.txt @@ -1,6 +1,10 @@ {{user_name|default:"Developer"}}, +{% if owner %} The booking you requested of the OPNFV Lab as a Service has finished deploying and is ready for you to use. +{% else %} +A booking you collaborate on is ready for you to use +{% endif %} The lab that fulfilled your booking request has sent you the following messages: {% for email_message in messages %} diff --git a/src/templates/notifier/expiring_booking.html b/src/templates/notifier/expiring_booking.html new file mode 100644 index 0000000..8bfa689 --- /dev/null +++ b/src/templates/notifier/expiring_booking.html @@ -0,0 +1,35 @@ +<html> + <body> + <div id="message_content_wrapper"> + {% if owner %} + <h3>Your booking is about to expire</h3> + <p>Your booking will expire within 48 hours ({{booking.end}}).</p> + {% else %} + <h3>A booking that you collaborate on is about to expire</h3> + <p>The booking owned by {{booking.owner.username}} that you work on is about to expire</p> + {% endif %} + <p>Please take the time to backup all data or extend the booking if needed.</p> + <p>Booking information:</p> + <ul> + <li>owner: {{booking.owner.username}}</li> + <li>id: {{booking.id}}</li> + <li>lab: {{booking.resource.template.lab.lab_user.username}}</li> + <li>resource: {{booking.resource.template.name}}</li> + <li>start: {{booking.start}}</li> + <li>end: {{booking.end}}</li> + <li>purpose: {{booking.purpose}}</li> + <li>collaborators: + <ul> + {% for user in booking.collaborators.all %} + <li>user.username</li> + {% empty %} + <li>No collaborators</li> + {% endfor %} + </ul> + </li> + </ul> + + <p>You can find more detailed information <a href=/booking/detail/{{booking.id}}/>Here</a></p> + </div> + </body> +</html> diff --git a/src/templates/notifier/new_booking.html b/src/templates/notifier/new_booking.html index d23b12e..64244e0 100644 --- a/src/templates/notifier/new_booking.html +++ b/src/templates/notifier/new_booking.html @@ -6,7 +6,7 @@ <p>We have recieved your booking request and will start working on it right away.</p> {% else %} <h3>You have been added as a collaborator to a booking</h3> - <p>{{booking.owner.username}} has given you access to thier booking.</p> + <p>{{booking.owner.username}} has given you access to their booking.</p> {% endif %} <p>Booking information:</p> <ul> |