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
Showing
53 changed files
with
1,607 additions
and
19 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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
*.pyc | ||
.idea/ | ||
.vagrant/ | ||
.vagrant/ | ||
.pycharm_helpers/ |
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 @@ | ||
Django Monitoring | ||
================= | ||
Extensive documentation will be provided soon. | ||
|
||
Improve django_monitoring | ||
------------------------- | ||
|
||
We've included an example app to show how django_monitoring works and to make it easy to improve it. | ||
Start by launching the included vagrant machine: | ||
```bash | ||
vagrant up | ||
``` | ||
|
||
Then setup the example app environment: | ||
```bash | ||
./manage.py migrate | ||
./manage.py loaddata example | ||
``` | ||
The installed superuser is "example" with password "monitoring". | ||
|
||
Run the development webserver: | ||
```bash | ||
./manage.py runserver 0.0.0.0:8000 | ||
``` | ||
|
||
Login on the admin interface and open http://dm.dev:8000/ afterwards. | ||
You'll be prompted with an empty dashboard. That's because we didn't run any checks yet. | ||
Let's enqueue an update: | ||
```bash | ||
./manage.py monitoring_run_checks | ||
``` | ||
|
||
Now we need to start a celery worker to handle the updates: | ||
```bash | ||
celery worker -A example -l DEBUG -Q django_monitoring | ||
``` | ||
|
||
You will see some failed check now after you refreshed the dashboard view. | ||
|
||
![Django monitoring dashboard](http://static.jensnistler.de/django_monitoring.png "Django monitoring dashboard") |
This file was deleted.
Oops, something went wrong.
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
Empty file.
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 @@ | ||
# -*- coding: UTF-8 -*- | ||
from __future__ import unicode_literals | ||
|
||
from django.contrib import admin | ||
|
||
from django_monitoring.models import Check | ||
|
||
|
||
@admin.register(Check) | ||
class CheckAdmin(admin.ModelAdmin): | ||
list_display = ('slug', 'identifier', 'status') | ||
search_fields = ('slug', 'identifier', 'payload_description') | ||
list_filter = ('status', ) |
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,17 @@ | ||
# -*- coding: UTF-8 -*- | ||
from __future__ import unicode_literals, print_function | ||
|
||
from django.apps import AppConfig | ||
from django.contrib import admin | ||
from django_monitoring.monitoring import monitor | ||
|
||
|
||
class DjangoMonitoringConfig(AppConfig): | ||
name = 'django_monitoring' | ||
verbose_name = "DjangoMonitoring" | ||
|
||
def ready(self): | ||
super(DjangoMonitoringConfig, self).ready() | ||
|
||
monitor.autodiscover_checks() | ||
admin.autodiscover() |
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,133 @@ | ||
# -*- coding: UTF-8 -*- | ||
from __future__ import unicode_literals | ||
import logging | ||
|
||
from django import forms | ||
|
||
from django_monitoring import models | ||
from django_monitoring.tasks import django_monitoring_enqueue | ||
from django_monitoring.models import Check | ||
from django_monitoring.monitoring import monitor | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class BaseCheckForm(forms.Form): | ||
def save(self, instance): | ||
instance.config = self.cleaned_data | ||
instance.save(update_fields=['config']) | ||
return instance | ||
|
||
|
||
class BaseCheck(object): | ||
|
||
""" | ||
Any check should inherits from `BaseCheck` and should implements `.generate(self)` | ||
and `.check(self, payload)` methods. | ||
Optionally, you can implements `.get_assigned_user(self, payload)` (resp. `.get_assigned_group(self, payload)`) | ||
to define to which user (resp. group) the system had to assign the check result. | ||
""" | ||
|
||
config_form = None | ||
title = '' | ||
|
||
def __init__(self): | ||
self.slug = monitor.get_slug(self.__module__, self.__class__.__name__) | ||
|
||
def run(self): | ||
django_monitoring_enqueue.apply_async(kwargs=dict(check_slug=self.slug), queue='django_monitoring') | ||
|
||
def handle(self, payload): | ||
# check result | ||
unacknowledge = False | ||
try: | ||
check_result = Check.objects.get( | ||
slug=self.slug, identifier=self.get_identifier(payload)) | ||
old_status = check_result.status | ||
except Check.DoesNotExist: | ||
check_result = None | ||
status = self.check(payload) | ||
if check_result and old_status > Check.STATUS.ok and status == Check.STATUS.ok: | ||
unacknowledge = True | ||
self.save(payload, status, | ||
unacknowledge=unacknowledge) | ||
|
||
def get_config(self, payload): | ||
try: | ||
check_result = Check.objects.get(slug=self.slug, identifier=self.get_identifier(payload)) | ||
|
||
# check has a configuration | ||
if check_result.config: | ||
return check_result.config | ||
except Check.DoesNotExist: | ||
pass | ||
|
||
# get default config from form initial values | ||
form = self.get_form_class()() | ||
return {name: field.initial for name, field in form.fields.items()} | ||
|
||
def get_form(self, payload): | ||
return self.get_form_class()(**self.get_config(payload)) | ||
|
||
def get_form_class(self): | ||
return self.config_form | ||
|
||
def save(self, payload, result, unacknowledge=False): | ||
defaults = { | ||
'status': result, | ||
'assigned_to_user': self.get_assigned_user(payload, result), | ||
'assigned_to_group': self.get_assigned_group(payload, result), | ||
'payload_description': self.get_payload_description(payload) | ||
} | ||
|
||
# save the check | ||
dataset, created = Check.objects.get_or_create( | ||
slug=self.slug, identifier=self.get_identifier(payload), | ||
defaults=defaults) | ||
|
||
# update existing dataset | ||
if not created: | ||
for (key, value) in defaults.items(): | ||
setattr(dataset, key, value) | ||
if unacknowledge: | ||
dataset.acknowledge_by = None | ||
dataset.acknowledge_at = None | ||
dataset.acknowledge_until = None | ||
dataset.save() | ||
|
||
def generate(self): | ||
""" | ||
yield items to run check for | ||
""" | ||
raise NotImplementedError(".generate() should be overridden") | ||
|
||
def check(self, payload): | ||
""" | ||
:param payload: the payload to run the check for | ||
:return: | ||
""" | ||
raise NotImplementedError(".check() should be overridden") | ||
|
||
def get_identifier(self, payload): | ||
raise NotImplementedError(".get_identifier() should be overridden") | ||
|
||
def get_payload_description(self, payload): | ||
return str(payload) | ||
|
||
def get_assigned_user(self, payload, result): | ||
return None | ||
|
||
def get_assigned_group(self, payload, result): | ||
return None | ||
|
||
def get_context_data(self, payload): | ||
return dict() | ||
|
||
def get_title(self): | ||
return self.title | ||
|
||
def get_template_name(self): | ||
if hasattr(self, 'template_name'): | ||
return self.template_name | ||
return None |
Empty file.
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,21 @@ | ||
# -*- coding: UTF-8 -*- | ||
from django.views.generic.edit import FormMixin | ||
from django.views.generic.list import ListView | ||
|
||
|
||
class FilteredListView(FormMixin, ListView): | ||
def get_form_kwargs(self): | ||
return { | ||
'initial': self.get_initial(), | ||
'prefix': self.get_prefix(), | ||
'data': self.request.GET or None | ||
} | ||
|
||
def get(self, request, *args, **kwargs): | ||
self.object_list = self.get_queryset() | ||
|
||
form = self.get_form(self.get_form_class()) | ||
self.object_list = form.filter_queryset(request, self.object_list) | ||
|
||
context = self.get_context_data(form=form, object_list=self.object_list) | ||
return self.render_to_response(context) |
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,58 @@ | ||
# -*- coding: UTF-8 -*- | ||
from django import forms | ||
from django.contrib.auth import get_user_model | ||
from django.utils.translation import ugettext_lazy as _ | ||
from model_utils.choices import Choices | ||
|
||
from django_monitoring.models import Check | ||
|
||
|
||
class ResultFilterForm(forms.Form): | ||
STATUS_CHOICES = Choices((0, 'all', _('All')), (1, 'failed', _('Failed'))) | ||
|
||
user = forms.ModelChoiceField(queryset=get_user_model().objects.all().order_by('last_name', 'first_name'), | ||
label=_('User'), required=False) | ||
status = forms.TypedChoiceField(coerce=int, choices=STATUS_CHOICES, label=_('Status'), | ||
initial=STATUS_CHOICES.failed) | ||
|
||
def __init__(self, user, group_filter=None, **kwargs): | ||
super(ResultFilterForm, self).__init__(**kwargs) | ||
|
||
if not group_filter: | ||
group_filter = dict() | ||
|
||
self.fields['user'].initial = user | ||
self.fields['user'].queryset = self.fields['user'].queryset.filter(**group_filter) | ||
|
||
def filter_queryset(self, request, queryset): | ||
# default values if form has not been submitted | ||
if not self.is_bound: | ||
return queryset.failed().unacknowledged().for_user(request.user) | ||
# form has been submitted with invalid values | ||
if not self.is_valid(): | ||
return queryset.none() | ||
|
||
if self.cleaned_data['user']: | ||
queryset = queryset.for_user(self.cleaned_data['user']) | ||
if self.cleaned_data['status']: | ||
if self.cleaned_data['status'] == self.STATUS_CHOICES.failed: | ||
queryset = queryset.failed().unacknowledged() | ||
|
||
return queryset.distinct() | ||
|
||
|
||
class AcknowledgeForm(forms.ModelForm): | ||
days = forms.IntegerField(label=_('Days to acknowledge')) | ||
|
||
class Meta: | ||
model = Check | ||
fields = ['days', 'acknowledged_reason'] | ||
|
||
def __init__(self, user, **kwargs): | ||
self.user = user | ||
super(AcknowledgeForm, self).__init__(**kwargs) | ||
|
||
def save(self, commit=True): | ||
self.instance.acknowledge(user=self.user, days=self.cleaned_data.get('days'), | ||
reason=self.cleaned_data['acknowledged_reason'], commit=commit) | ||
return self.instance |
Binary file not shown.
Oops, something went wrong.