Skip to content
This repository has been archived by the owner on Feb 8, 2019. It is now read-only.

Commit

Permalink
initial public release
Browse files Browse the repository at this point in the history
  • Loading branch information
deathowl committed May 22, 2014
0 parents commit 736d383
Show file tree
Hide file tree
Showing 76 changed files with 3,241 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.idea/*
env/*
*.pyc
database.sql
.DS_Store
43 changes: 43 additions & 0 deletions README.md
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```
40 changes: 40 additions & 0 deletions extra/settings_prod.py.example
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']
5 changes: 5 additions & 0 deletions extra/voice.xml
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>

74 changes: 74 additions & 0 deletions extra/wrapper.sh
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"
13 changes: 13 additions & 0 deletions license.txt
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.
10 changes: 10 additions & 0 deletions manage.py
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)
1 change: 1 addition & 0 deletions notification/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__author__ = 'oker'
92 changes: 92 additions & 0 deletions notification/helper.py
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
64 changes: 64 additions & 0 deletions notification/models.py
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)

1 change: 1 addition & 0 deletions notification/notifier/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__author__ = 'oker'
27 changes: 27 additions & 0 deletions notification/notifier/email.py
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"
Loading

0 comments on commit 736d383

Please sign in to comment.