This repository has been archived by the owner on Feb 8, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
deathowl
committed
May 22, 2014
0 parents
commit 736d383
Showing
76 changed files
with
3,241 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.idea/* | ||
env/* | ||
*.pyc | ||
database.sql | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#What is this? | ||
**Openduty** is an incident escalation tool, just like [Pagerduty](http://pagerduty.com) . It has a Pagerduty compatible API too. It's the result of the first [Ustream Hackathon](http://www.ustream.tv/blog/2014/03/27/hackathon-recap-21-ideas-11-teams-one-goal/). We enjoyed working on it. | ||
#Integrations | ||
Has been tested with Nagios, works well for us. Any Pagerduty Notifier using the Pagerduty API should work without a problem. | ||
#Notifications | ||
XMPP, email, SMS, Phone(Thanks Twilio for being awesome!), and Push notifications(thanks Pushover also) are supported at the moment. | ||
#Current status | ||
Openduty is in Beta status, it can be considered stable at the moment, however major structural changes can appear anytime (not affecting the API, or the Notifier structure) | ||
|
||
#Contribution guidelines | ||
Yes, please. You are welcome. | ||
#Feedback | ||
Any feedback is welcome | ||
|
||
#Contributors at Ustream | ||
- [deathowl](http://github.com/deathowl) | ||
- [oker](http://github.com/oker1) | ||
- [tyrael](http://github.com/tyrael) | ||
- [dzsubek](https://github.com/dzsubek) | ||
- [ecsy](https://github.com/ecsy) | ||
- [akos](https://github.com/gyim) | ||
|
||
![The team](http://deathowlsnest.com/images/cod.jpg) | ||
|
||
# Getting started: | ||
``` | ||
sudo easy_install pip | ||
sudo pip install virtualenv | ||
virtualenv env | ||
. env/bin/activate | ||
pip install -r requirements.txt | ||
export DJANGO_SETTINGS_MODULE=openduty.settings_dev | ||
python manage.py syncdb | ||
python manage.py runserver | ||
``` | ||
now, you can start hacking on it. | ||
|
||
|
||
# Default login: | ||
root/toor | ||
|
||
# Celery worker: | ||
```celery -A openduty worker -l info``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from settings import * | ||
|
||
|
||
BASE_URL = "https://your.dutyfree.host" | ||
|
||
XMPP_SETTINGS = { | ||
'user': "alerting@yourorganisation.org", | ||
'password': "YourPassword", | ||
'server': 'your.xmpp.host', | ||
'port': 5222, | ||
} | ||
|
||
EMAIL_SETTINGS = { | ||
'user': "alerting@yourorganisation.org", | ||
'password': "YourPassword", | ||
} | ||
|
||
TWILIO_SETTINGS = { | ||
'SID': "TWILIO_ACCOUNT_SID", | ||
'token': "TWILIO_ACCOUNT_TOKEN", | ||
'phone_number': "your_twilio_phone_number", | ||
'sms_number': "your_twilio_sms_number", | ||
'twiml_url': "http://www.website.org/voice.xml" | ||
} | ||
|
||
DATABASES = { | ||
'default': { | ||
'ENGINE': 'django.db.backends.mysql', | ||
'NAME': 'yourmysqluser', | ||
'USER': 'yourdbname', | ||
'PASSWORD': 'yourdbpassword', | ||
'HOST': 'yourdbhost', | ||
'PORT': 'yourdbport' | ||
} | ||
} | ||
|
||
# SECURITY WARNING: keep the secret key used in production secret! | ||
SECRET_KEY = 'yoursecretkey' | ||
|
||
ALLOWED_HOSTS = ['your.dutyfree.host'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Response> | ||
<Say>A Service is having problems, please see the openduty web interface for details</Say> | ||
</Response> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#! /bin/bash | ||
usage() | ||
{ | ||
cat << EOF | ||
usage: $0 options | ||
This script is a wrapper for pagerduty_nagios.pl | ||
OPTIONS: | ||
-h Show this message | ||
-t Notification type can be ‘PROBLEM′ or ‘RECOVERY′ | ||
-k Service key | ||
-i Incident key | ||
-d Description | ||
-e Details | ||
EOF | ||
} | ||
|
||
|
||
|
||
|
||
declare TYPE= | ||
declare API_BASE="your.dutyfree.host" | ||
declare SERV_KEY= | ||
declare INCIDENT_KEY= | ||
declare DESCRIPTION= | ||
declare DETAILS= | ||
while getopts “ht:k:i:d:e:” OPTION | ||
do | ||
case $OPTION in | ||
h) | ||
usage | ||
exit 1 | ||
;; | ||
t) | ||
TYPE=$OPTARG | ||
;; | ||
k) | ||
SERV_KEY=$OPTARG | ||
;; | ||
i) | ||
INCIDENT_KEY=$OPTARG | ||
;; | ||
d) | ||
DESCRIPTION=$OPTARG | ||
;; | ||
e) | ||
DETAILS=$OPTARG | ||
;; | ||
?) | ||
usage | ||
exit | ||
;; | ||
esac | ||
done | ||
|
||
if [[ -z $TYPE ]] || [[ -z $SERV_KEY ]] || [[ -z $INCIDENT_KEY ]] || [[ -z $DESCRIPTION ]] || [[ -z $DETAILS ]] | ||
then | ||
echo "mandatory parameter missing :(" | ||
usage | ||
exit 1 | ||
fi | ||
|
||
if [ $TYPE == "PROBLEM" ] || [ $TYPE == "CUSTOM" ] | ||
then | ||
ACTION=trigger | ||
elif [ $TYPE == "ACKNOWLEDGEMENT" ] | ||
then | ||
ACTION=acknowledge | ||
else | ||
ACTION=resolve | ||
fi | ||
|
||
perl /opt/dutyfree-nagios-pl/pagerduty_nagios.pl enqueue --api-base=$API_BASE -f service_key=$SERV_KEY -f incident_key=$INCIDENT_KEY -f description=$DESCRIPTION -f event_type=$ACTION -f details="$DETAILS" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
Version 2, December 2004 | ||
|
||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | ||
|
||
Everyone is permitted to copy and distribute verbatim or modified | ||
copies of this license document, and changing it is allowed as long | ||
as the name is changed. | ||
|
||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
|
||
0. You just DO WHAT THE FUCK YOU WANT TO. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/usr/bin/env python | ||
import os | ||
import sys | ||
|
||
if __name__ == "__main__": | ||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openduty.settings") | ||
|
||
from django.core.management import execute_from_command_line | ||
|
||
execute_from_command_line(sys.argv) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__author__ = 'oker' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
__author__ = 'deathowl' | ||
|
||
from datetime import datetime, timedelta | ||
|
||
from notification.tasks import send_notifications | ||
from openduty.escalation_helper import get_escalation_for_service | ||
from django.utils import timezone | ||
from notification.models import ScheduledNotification | ||
from django.conf import settings | ||
|
||
|
||
class NotificationHelper(object): | ||
@staticmethod | ||
def notify_incident(incident): | ||
notifications = NotificationHelper.generate_notifications_for_incident(incident) | ||
|
||
for notification in notifications: | ||
notification.save() | ||
send_notifications.apply_async((notification.id,) ,eta=notification.send_at) | ||
@staticmethod | ||
def notify_user_about_incident(incident, user): | ||
notifications = NotificationHelper.generate_notifications_for_user(incident, user) | ||
|
||
for notification in notifications: | ||
notification.save() | ||
send_notifications.apply_async((notification.id,) ,eta=notification.send_at) | ||
|
||
@staticmethod | ||
def generate_notifications_for_incident(incident): | ||
now = timezone.make_aware(datetime.now(), timezone.get_current_timezone()) | ||
duty_officers = get_escalation_for_service(incident.service_key) | ||
|
||
current_time = now | ||
|
||
notifications = [] | ||
|
||
for officer_index, duty_officer in enumerate(duty_officers): | ||
escalation_time = incident.service_key.escalate_after * (officer_index + 1) | ||
escalate_at = current_time + timedelta(minutes=escalation_time) | ||
|
||
methods = duty_officer.notification_methods.all() | ||
method_index = 0 | ||
|
||
for method in methods: | ||
notification_time = incident.service_key.retry * method_index + incident.service_key.escalate_after * officer_index | ||
notify_at = current_time + timedelta(minutes=notification_time) | ||
if notify_at < escalate_at: | ||
notification = ScheduledNotification() | ||
notification.incident = incident | ||
notification.user_to_notify = duty_officer | ||
notification.notifier = method.method | ||
notification.send_at = notify_at | ||
uri = settings.BASE_URL + "/incidents/details/" + str(incident.id) | ||
notification.message = "A Service is experiencing a problem: " + incident.incident_key + " " + incident.description + ". Handle at: " + uri | ||
|
||
notifications.append(notification) | ||
|
||
print "Notify %s at %s with method: %s" % (duty_officer.username, notify_at, notification.notifier) | ||
else: | ||
break | ||
method_index += 1 | ||
|
||
# todo: error handling | ||
|
||
return notifications | ||
|
||
@staticmethod | ||
def generate_notifications_for_user(incident, user): | ||
|
||
now = timezone.make_aware(datetime.now(), timezone.get_current_timezone()) | ||
current_time = now | ||
notifications = [] | ||
methods = user.notification_methods.all() | ||
method_index = 0 | ||
|
||
for method in methods: | ||
notification_time = incident.service_key.retry * method_index + incident.service_key.escalate_after | ||
notify_at = current_time + timedelta(minutes=notification_time) | ||
notification = ScheduledNotification() | ||
notification.incident = incident | ||
notification.user_to_notify = user | ||
notification.notifier = method.method | ||
notification.send_at = notify_at | ||
uri = settings.BASE_URL + "/incidents/details/" + str(incident.id) | ||
notification.message = "A Service is experiencing a problem: " + incident.incident_key + " " + incident.description + ". Handle at: " + uri | ||
|
||
notifications.append(notification) | ||
print "Notify %s at %s with method: %s" % (user.username, notify_at, notification.notifier) | ||
method_index += 1 | ||
|
||
# todo: error handling | ||
return notifications |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import datetime | ||
import dateutil | ||
from django.contrib.auth.models import User | ||
from django.utils.encoding import python_2_unicode_compatible | ||
from django.db import models | ||
from django.utils.translation import ugettext_lazy as _ | ||
from openduty.models import Incident | ||
|
||
|
||
@python_2_unicode_compatible | ||
class UserNotificationMethod(models.Model): | ||
""" | ||
Schedule rule | ||
""" | ||
|
||
METHOD_TWILIO_SMS = 'twilio_sms' | ||
METHOD_TWILIO_CALL = 'twilio_call' | ||
METHOD_EMAIL = 'email' | ||
METHOD_PUSHOVER = 'pushover' | ||
METHOD_XMPP = 'xmpp' | ||
|
||
methods = [METHOD_XMPP, METHOD_PUSHOVER, METHOD_EMAIL, METHOD_TWILIO_SMS, METHOD_TWILIO_CALL] | ||
|
||
user = models.ForeignKey(User, related_name='notification_methods') | ||
position = models.IntegerField() | ||
method = models.CharField(max_length=50) | ||
|
||
class Meta: | ||
verbose_name = _('user_notification_method') | ||
verbose_name_plural = _('user_notification_methods') | ||
db_table = 'openduty_usernotificationmethod' | ||
|
||
def __str__(self): | ||
return self.id | ||
|
||
|
||
@python_2_unicode_compatible | ||
class ScheduledNotification(models.Model): | ||
notifier = models.CharField(max_length=30) | ||
message = models.CharField(max_length=500) | ||
user_to_notify = models.ForeignKey(User) | ||
send_at = models.DateTimeField() | ||
incident = models.ForeignKey(Incident) | ||
|
||
class Meta: | ||
verbose_name = _('scheduled_notifications') | ||
verbose_name_plural = _('scheduled_notifications') | ||
db_table = 'openduty_schedulednotification' | ||
|
||
def __str__(self): | ||
return self.id | ||
|
||
@staticmethod | ||
def remove_all_for_incident(incident): | ||
notices = ScheduledNotification.objects.filter(incident=incident) | ||
for notice in notices: | ||
notice.delete() | ||
|
||
@staticmethod | ||
def get_notifications_to_send(date=None): | ||
if not date: | ||
date = datetime.datetime.now(dateutil.tz.tzutc()) | ||
return ScheduledNotification.objects.filter(send_at__lte=date) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__author__ = 'oker' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
__author__ = 'deathowl' | ||
|
||
import smtplib | ||
|
||
class EmailNotifier: | ||
def __init__(self, config): | ||
self.__config = config | ||
def notify(self, notification): | ||
|
||
gmail_user = self.__config['user'] | ||
gmail_pwd = self.__config['password'] | ||
FROM = self.__config['user'] | ||
TO = [notification.user_to_notify.email] | ||
SUBJECT = "Openduty Incident Report" | ||
TEXT = notification.message | ||
message = """\From: %s\nTo: %s\nSubject: %s\n\n%s | ||
""" % (FROM, ", ".join(TO), SUBJECT, TEXT) | ||
try: | ||
server = smtplib.SMTP("smtp.gmail.com", 587) | ||
server.starttls() | ||
server.ehlo() | ||
server.login(gmail_user, gmail_pwd) | ||
server.sendmail(FROM, TO, message) | ||
server.close() | ||
print 'successfully sent the mail' | ||
except: | ||
print "failed to send mail" |
Oops, something went wrong.