mirror of
https://github.com/status-im/cabot.git
synced 2025-02-24 18:38:07 +00:00
Merge branch 'jmontineri-instances'
This commit is contained in:
commit
cab908b24f
@ -1,5 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import UserProfile, Service, Shift, ServiceStatusSnapshot, StatusCheck, StatusCheckResult
|
||||
from .models import UserProfile, Service, Shift, ServiceStatusSnapshot, StatusCheck, StatusCheckResult, Instance
|
||||
|
||||
admin.site.register(UserProfile)
|
||||
admin.site.register(Shift)
|
||||
@ -7,3 +7,4 @@ admin.site.register(Service)
|
||||
admin.site.register(ServiceStatusSnapshot)
|
||||
admin.site.register(StatusCheck)
|
||||
admin.site.register(StatusCheckResult)
|
||||
admin.site.register(Instance)
|
||||
|
205
app/cabotapp/migrations/0005_auto__add_instance.py
Normal file
205
app/cabotapp/migrations/0005_auto__add_instance.py
Normal file
@ -0,0 +1,205 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'Instance'
|
||||
db.create_table('cabotapp_instance', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.TextField')()),
|
||||
('last_alert_sent', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
('email_alert', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('hipchat_alert', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
('sms_alert', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('telephone_alert', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('alerts_enabled', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
('overall_status', self.gf('django.db.models.fields.TextField')(default='PASSING')),
|
||||
('old_overall_status', self.gf('django.db.models.fields.TextField')(default='PASSING')),
|
||||
('hackpad_id', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
|
||||
('address', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
))
|
||||
db.send_create_signal('cabotapp', ['Instance'])
|
||||
|
||||
# Adding M2M table for field users_to_notify on 'Instance'
|
||||
db.create_table('cabotapp_instance_users_to_notify', (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('instance', models.ForeignKey(orm['cabotapp.instance'], null=False)),
|
||||
('user', models.ForeignKey(orm['auth.user'], null=False))
|
||||
))
|
||||
db.create_unique('cabotapp_instance_users_to_notify', ['instance_id', 'user_id'])
|
||||
|
||||
# Adding M2M table for field status_checks on 'Instance'
|
||||
db.create_table('cabotapp_instance_status_checks', (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('instance', models.ForeignKey(orm['cabotapp.instance'], null=False)),
|
||||
('statuscheck', models.ForeignKey(orm['cabotapp.statuscheck'], null=False))
|
||||
))
|
||||
db.create_unique('cabotapp_instance_status_checks', ['instance_id', 'statuscheck_id'])
|
||||
|
||||
# Adding M2M table for field instances on 'Service'
|
||||
db.create_table('cabotapp_service_instances', (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('service', models.ForeignKey(orm['cabotapp.service'], null=False)),
|
||||
('instance', models.ForeignKey(orm['cabotapp.instance'], null=False))
|
||||
))
|
||||
db.create_unique('cabotapp_service_instances', ['service_id', 'instance_id'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Instance'
|
||||
db.delete_table('cabotapp_instance')
|
||||
|
||||
# Removing M2M table for field users_to_notify on 'Instance'
|
||||
db.delete_table('cabotapp_instance_users_to_notify')
|
||||
|
||||
# Removing M2M table for field status_checks on 'Instance'
|
||||
db.delete_table('cabotapp_instance_status_checks')
|
||||
|
||||
# Removing M2M table for field instances on 'Service'
|
||||
db.delete_table('cabotapp_service_instances')
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'cabotapp.instance': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Instance'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'cabotapp.service': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Service'},
|
||||
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instances': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.Instance']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'url': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'cabotapp.servicestatussnapshot': {
|
||||
'Meta': {'object_name': 'ServiceStatusSnapshot'},
|
||||
'did_send_alert': ('django.db.models.fields.IntegerField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'num_checks_active': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_failing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_passing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['cabotapp.Service']"}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.shift': {
|
||||
'Meta': {'object_name': 'Shift'},
|
||||
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'end': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'start': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'uid': ('django.db.models.fields.TextField', [], {}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'cabotapp.statuscheck': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'StatusCheck'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cached_health': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'calculated_status': ('django.db.models.fields.CharField', [], {'default': "'passing'", 'max_length': '50', 'blank': 'True'}),
|
||||
'check_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
|
||||
'debounce': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
|
||||
'endpoint': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'expected_num_hosts': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
|
||||
'frequency': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'importance': ('django.db.models.fields.CharField', [], {'default': "'ERROR'", 'max_length': '30'}),
|
||||
'last_run': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'max_queued_build_time': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'metric': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'password': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_cabotapp.statuscheck_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
|
||||
'status_code': ('django.db.models.fields.TextField', [], {'default': '200', 'null': 'True'}),
|
||||
'text_match': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'timeout': ('django.db.models.fields.IntegerField', [], {'default': '30', 'null': 'True'}),
|
||||
'username': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'value': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'verify_ssl_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'cabotapp.statuscheckresult': {
|
||||
'Meta': {'object_name': 'StatusCheckResult'},
|
||||
'check': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cabotapp.StatusCheck']"}),
|
||||
'error': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'raw_data': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'succeeded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'time_complete': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.userprofile': {
|
||||
'Meta': {'object_name': 'UserProfile'},
|
||||
'fallback_alert_user': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hipchat_alias': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'mobile_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['cabotapp']
|
@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
|
||||
# Changing field 'StatusCheck.created_by'
|
||||
db.alter_column('cabotapp_statuscheck', 'created_by_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True))
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Changing field 'StatusCheck.created_by'
|
||||
db.alter_column('cabotapp_statuscheck', 'created_by_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['auth.User']))
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'cabotapp.instance': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Instance'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'cabotapp.service': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Service'},
|
||||
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instances': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.Instance']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'url': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'cabotapp.servicestatussnapshot': {
|
||||
'Meta': {'object_name': 'ServiceStatusSnapshot'},
|
||||
'did_send_alert': ('django.db.models.fields.IntegerField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'num_checks_active': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_failing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_passing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['cabotapp.Service']"}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.shift': {
|
||||
'Meta': {'object_name': 'Shift'},
|
||||
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'end': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'start': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'uid': ('django.db.models.fields.TextField', [], {}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'cabotapp.statuscheck': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'StatusCheck'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cached_health': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'calculated_status': ('django.db.models.fields.CharField', [], {'default': "'passing'", 'max_length': '50', 'blank': 'True'}),
|
||||
'check_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
|
||||
'debounce': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
|
||||
'endpoint': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'expected_num_hosts': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
|
||||
'frequency': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'importance': ('django.db.models.fields.CharField', [], {'default': "'ERROR'", 'max_length': '30'}),
|
||||
'last_run': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'max_queued_build_time': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'metric': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'password': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_cabotapp.statuscheck_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
|
||||
'status_code': ('django.db.models.fields.TextField', [], {'default': '200', 'null': 'True'}),
|
||||
'text_match': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'timeout': ('django.db.models.fields.IntegerField', [], {'default': '30', 'null': 'True'}),
|
||||
'username': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'value': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'verify_ssl_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'cabotapp.statuscheckresult': {
|
||||
'Meta': {'object_name': 'StatusCheckResult'},
|
||||
'check': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cabotapp.StatusCheck']"}),
|
||||
'error': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'raw_data': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'succeeded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'time_complete': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.userprofile': {
|
||||
'Meta': {'object_name': 'UserProfile'},
|
||||
'fallback_alert_user': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hipchat_alias': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'mobile_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['cabotapp']
|
179
app/cabotapp/migrations/0007_auto__add_instancestatussnapshot.py
Normal file
179
app/cabotapp/migrations/0007_auto__add_instancestatussnapshot.py
Normal file
@ -0,0 +1,179 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'InstanceStatusSnapshot'
|
||||
db.create_table('cabotapp_instancestatussnapshot', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('instance', self.gf('django.db.models.fields.related.ForeignKey')(related_name='snapshots', to=orm['cabotapp.Instance'])),
|
||||
('time', self.gf('django.db.models.fields.DateTimeField')(db_index=True)),
|
||||
('num_checks_active', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('num_checks_passing', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('num_checks_failing', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('overall_status', self.gf('django.db.models.fields.TextField')(default='PASSING')),
|
||||
('did_send_alert', self.gf('django.db.models.fields.IntegerField')(default=False)),
|
||||
))
|
||||
db.send_create_signal('cabotapp', ['InstanceStatusSnapshot'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'InstanceStatusSnapshot'
|
||||
db.delete_table('cabotapp_instancestatussnapshot')
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'cabotapp.instance': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Instance'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'cabotapp.instancestatussnapshot': {
|
||||
'Meta': {'object_name': 'InstanceStatusSnapshot'},
|
||||
'did_send_alert': ('django.db.models.fields.IntegerField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['cabotapp.Instance']"}),
|
||||
'num_checks_active': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_failing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_passing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.service': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Service'},
|
||||
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'instances': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.Instance']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'url': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'cabotapp.servicestatussnapshot': {
|
||||
'Meta': {'object_name': 'ServiceStatusSnapshot'},
|
||||
'did_send_alert': ('django.db.models.fields.IntegerField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'num_checks_active': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_failing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'num_checks_passing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
|
||||
'service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['cabotapp.Service']"}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.shift': {
|
||||
'Meta': {'object_name': 'Shift'},
|
||||
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'end': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'start': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'uid': ('django.db.models.fields.TextField', [], {}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'cabotapp.statuscheck': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'StatusCheck'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'cached_health': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'calculated_status': ('django.db.models.fields.CharField', [], {'default': "'passing'", 'max_length': '50', 'blank': 'True'}),
|
||||
'check_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
|
||||
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
|
||||
'debounce': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
|
||||
'endpoint': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'expected_num_hosts': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
|
||||
'frequency': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'importance': ('django.db.models.fields.CharField', [], {'default': "'ERROR'", 'max_length': '30'}),
|
||||
'last_run': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'max_queued_build_time': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'metric': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'name': ('django.db.models.fields.TextField', [], {}),
|
||||
'password': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_cabotapp.statuscheck_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
|
||||
'status_code': ('django.db.models.fields.TextField', [], {'default': '200', 'null': 'True'}),
|
||||
'text_match': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'timeout': ('django.db.models.fields.IntegerField', [], {'default': '30', 'null': 'True'}),
|
||||
'username': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'value': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'verify_ssl_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'cabotapp.statuscheckresult': {
|
||||
'Meta': {'object_name': 'StatusCheckResult'},
|
||||
'check': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cabotapp.StatusCheck']"}),
|
||||
'error': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'raw_data': ('django.db.models.fields.TextField', [], {'null': 'True'}),
|
||||
'succeeded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'time_complete': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'})
|
||||
},
|
||||
'cabotapp.userprofile': {
|
||||
'Meta': {'object_name': 'UserProfile'},
|
||||
'fallback_alert_user': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'hipchat_alias': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'mobile_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['cabotapp']
|
@ -3,20 +3,24 @@ from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from polymorphic import PolymorphicModel
|
||||
from django.db.models import F
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.admin.models import User
|
||||
from celery.exceptions import SoftTimeLimitExceeded
|
||||
|
||||
from jenkins import get_job_status
|
||||
from .alert import send_alert
|
||||
from .calendar import get_events
|
||||
from .graphite import parse_metric
|
||||
from .alert import send_alert
|
||||
from .tasks import update_service
|
||||
from .tasks import update_service, update_instance
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils import timezone
|
||||
from django.db import transaction
|
||||
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import requests
|
||||
from celery.utils.log import get_task_logger
|
||||
@ -65,7 +69,11 @@ def calculate_debounced_passing(recent_results, debounce=0):
|
||||
return False
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
class CheckGroupMixin(models.Model):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
PASSING_STATUS = 'PASSING'
|
||||
WARNING_STATUS = 'WARNING'
|
||||
ERROR_STATUS = 'ERROR'
|
||||
@ -88,15 +96,16 @@ class Service(models.Model):
|
||||
)
|
||||
|
||||
name = models.TextField()
|
||||
url = models.TextField(
|
||||
blank=True,
|
||||
help_text="URL of service."
|
||||
)
|
||||
|
||||
users_to_notify = models.ManyToManyField(
|
||||
User,
|
||||
blank=True,
|
||||
help_text='Users who should receive alerts.',
|
||||
)
|
||||
alerts_enabled = models.BooleanField(
|
||||
default=True,
|
||||
help_text='Alert when this service is not healthy.',
|
||||
)
|
||||
status_checks = models.ManyToManyField(
|
||||
'StatusCheck',
|
||||
blank=True,
|
||||
@ -113,10 +122,6 @@ class Service(models.Model):
|
||||
default=False,
|
||||
help_text='Must be enabled, and check importance set to Critical, to receive telephone alerts.',
|
||||
)
|
||||
alerts_enabled = models.BooleanField(
|
||||
default=True,
|
||||
help_text='Alert when this service is not healthy.',
|
||||
)
|
||||
overall_status = models.TextField(default=PASSING_STATUS)
|
||||
old_overall_status = models.TextField(default=PASSING_STATUS)
|
||||
hackpad_id = models.TextField(
|
||||
@ -126,30 +131,10 @@ class Service(models.Model):
|
||||
help_text='Gist, Hackpad or Refheap js embed with recovery instructions e.g. https://you.hackpad.com/some_document.js'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def update_status(self):
|
||||
self.old_overall_status = self.overall_status
|
||||
# Only active checks feed into our calculation
|
||||
status_checks_failed_count = self.all_failing_checks().count()
|
||||
self.overall_status = self.most_severe(self.all_failing_checks())
|
||||
self.snapshot = ServiceStatusSnapshot(
|
||||
service=self,
|
||||
num_checks_active=self.active_status_checks().count(),
|
||||
num_checks_passing=self.active_status_checks(
|
||||
).count() - status_checks_failed_count,
|
||||
num_checks_failing=status_checks_failed_count,
|
||||
overall_status=self.overall_status,
|
||||
time=timezone.now(),
|
||||
)
|
||||
self.snapshot.save()
|
||||
self.save()
|
||||
if not (self.overall_status == Service.PASSING_STATUS and self.old_overall_status == Service.PASSING_STATUS):
|
||||
self.alert()
|
||||
|
||||
def most_severe(self, check_list):
|
||||
failures = [c.importance for c in check_list]
|
||||
@ -189,7 +174,6 @@ class Service(models.Model):
|
||||
self.save()
|
||||
self.snapshot.did_send_alert = True
|
||||
self.snapshot.save()
|
||||
# send_alert handles the logic of how exactly alerts should be handled
|
||||
send_alert(self, duty_officers=get_duty_officers())
|
||||
|
||||
@property
|
||||
@ -201,18 +185,6 @@ class Service(models.Model):
|
||||
s['time'] = time.mktime(s['time'].timetuple())
|
||||
return snapshots
|
||||
|
||||
def active_status_checks(self):
|
||||
return self.status_checks.filter(active=True)
|
||||
|
||||
def inactive_status_checks(self):
|
||||
return self.status_checks.filter(active=False)
|
||||
|
||||
def all_passing_checks(self):
|
||||
return self.active_status_checks().filter(calculated_status=self.CALCULATED_PASSING_STATUS)
|
||||
|
||||
def all_failing_checks(self):
|
||||
return self.active_status_checks().exclude(calculated_status=self.CALCULATED_PASSING_STATUS)
|
||||
|
||||
def graphite_status_checks(self):
|
||||
return self.status_checks.filter(polymorphic_ctype__model='graphitestatuscheck')
|
||||
|
||||
@ -231,9 +203,110 @@ class Service(models.Model):
|
||||
def active_jenkins_status_checks(self):
|
||||
return self.jenkins_status_checks().filter(active=True)
|
||||
|
||||
def active_status_checks(self):
|
||||
return self.status_checks.filter(active=True)
|
||||
|
||||
def inactive_status_checks(self):
|
||||
return self.status_checks.filter(active=False)
|
||||
|
||||
def all_passing_checks(self):
|
||||
return self.active_status_checks().filter(calculated_status=self.CALCULATED_PASSING_STATUS)
|
||||
|
||||
def all_failing_checks(self):
|
||||
return self.active_status_checks().exclude(calculated_status=self.CALCULATED_PASSING_STATUS)
|
||||
|
||||
class Service(CheckGroupMixin):
|
||||
|
||||
def update_status(self):
|
||||
self.old_overall_status = self.overall_status
|
||||
# Only active checks feed into our calculation
|
||||
status_checks_failed_count = self.all_failing_checks().count()
|
||||
self.overall_status = self.most_severe(self.all_failing_checks())
|
||||
self.snapshot = ServiceStatusSnapshot(
|
||||
service=self,
|
||||
num_checks_active=self.active_status_checks().count(),
|
||||
num_checks_passing=self.active_status_checks(
|
||||
).count() - status_checks_failed_count,
|
||||
num_checks_failing=status_checks_failed_count,
|
||||
overall_status=self.overall_status,
|
||||
time=timezone.now(),
|
||||
)
|
||||
self.snapshot.save()
|
||||
self.save()
|
||||
if not (self.overall_status == Service.PASSING_STATUS and self.old_overall_status == Service.PASSING_STATUS):
|
||||
self.alert()
|
||||
instances = models.ManyToManyField(
|
||||
'Instance',
|
||||
blank=True,
|
||||
help_text='Instances this service is running on.',
|
||||
)
|
||||
|
||||
url = models.TextField(
|
||||
blank=True,
|
||||
help_text="URL of service."
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
|
||||
class Instance(CheckGroupMixin):
|
||||
|
||||
|
||||
def duplicate(self):
|
||||
checks = self.status_checks.all()
|
||||
new_instance = self
|
||||
new_instance.pk = None
|
||||
new_instance.id = None
|
||||
new_instance.name = "Copy of %s" % self.name
|
||||
|
||||
new_instance.save()
|
||||
|
||||
for check in checks:
|
||||
check.duplicate(inst_set=[new_instance], serv_set=())
|
||||
|
||||
return new_instance.pk
|
||||
|
||||
def update_status(self):
|
||||
self.old_overall_status = self.overall_status
|
||||
# Only active checks feed into our calculation
|
||||
status_checks_failed_count = self.all_failing_checks().count()
|
||||
self.overall_status = self.most_severe(self.all_failing_checks())
|
||||
self.snapshot = InstanceStatusSnapshot(
|
||||
instance=self,
|
||||
num_checks_active=self.active_status_checks().count(),
|
||||
num_checks_passing=self.active_status_checks(
|
||||
).count() - status_checks_failed_count,
|
||||
num_checks_failing=status_checks_failed_count,
|
||||
overall_status=self.overall_status,
|
||||
time=timezone.now(),
|
||||
)
|
||||
self.snapshot.save()
|
||||
self.save()
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
address = models.TextField(
|
||||
blank=True,
|
||||
help_text="Address (IP/Hostname) of service."
|
||||
)
|
||||
|
||||
def icmp_status_checks(self):
|
||||
return self.status_checks.filter(polymorphic_ctype__model='icmpstatuscheck')
|
||||
|
||||
def active_icmp_status_checks(self):
|
||||
return self.icmp_status_checks().filter(active=True)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
self.icmp_status_checks().delete()
|
||||
return super(Instance, self).delete(*args, **kwargs)
|
||||
|
||||
class Snapshot(models.Model):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class ServiceStatusSnapshot(models.Model):
|
||||
service = models.ForeignKey(Service, related_name='snapshots')
|
||||
time = models.DateTimeField(db_index=True)
|
||||
num_checks_active = models.IntegerField(default=0)
|
||||
num_checks_passing = models.IntegerField(default=0)
|
||||
@ -241,9 +314,17 @@ class ServiceStatusSnapshot(models.Model):
|
||||
overall_status = models.TextField(default=Service.PASSING_STATUS)
|
||||
did_send_alert = models.IntegerField(default=False)
|
||||
|
||||
class ServiceStatusSnapshot(Snapshot):
|
||||
service = models.ForeignKey(Service, related_name='snapshots')
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s: %s" % (self.service.name, self.overall_status)
|
||||
|
||||
class InstanceStatusSnapshot(Snapshot):
|
||||
instance = models.ForeignKey(Instance, related_name='snapshots')
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s: %s" % (self.instance.name, self.overall_status)
|
||||
|
||||
class StatusCheck(PolymorphicModel):
|
||||
|
||||
@ -278,7 +359,7 @@ class StatusCheck(PolymorphicModel):
|
||||
null=True,
|
||||
help_text='Number of successive failures permitted before check will be marked as failed. Default is 0, i.e. fail on first failure.'
|
||||
)
|
||||
created_by = models.ForeignKey(User)
|
||||
created_by = models.ForeignKey(User, null=True)
|
||||
calculated_status = models.CharField(
|
||||
max_length=50, choices=Service.STATUSES, default=Service.CALCULATED_PASSING_STATUS, blank=True)
|
||||
last_run = models.DateTimeField(null=True)
|
||||
@ -353,11 +434,11 @@ class StatusCheck(PolymorphicModel):
|
||||
return self.name
|
||||
|
||||
def recent_results(self):
|
||||
return self.statuscheckresult_set.all().order_by('-time_complete').defer('raw_data')[:10]
|
||||
return StatusCheckResult.objects.filter(check=self).order_by('-time_complete').defer('raw_data')[:10]
|
||||
|
||||
def last_result(self):
|
||||
try:
|
||||
return self.recent_results()[0]
|
||||
return StatusCheckResult.objects.filter(check=self).order_by('-time_complete').defer('raw_data')[0]
|
||||
except:
|
||||
return None
|
||||
|
||||
@ -365,6 +446,10 @@ class StatusCheck(PolymorphicModel):
|
||||
start = timezone.now()
|
||||
try:
|
||||
result = self._run()
|
||||
except SoftTimeLimitExceeded as e:
|
||||
result = StatusCheckResult(check=self)
|
||||
result.error = u'Error in performing check: Celery soft time limit exceeded'
|
||||
result.succeeded = False
|
||||
except Exception as e:
|
||||
result = StatusCheckResult(check=self)
|
||||
result.error = u'Error in performing check: %s' % (e,)
|
||||
@ -383,22 +468,90 @@ class StatusCheck(PolymorphicModel):
|
||||
raise NotImplementedError('Subclasses should implement')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
recent_results = self.recent_results()
|
||||
if calculate_debounced_passing(recent_results, self.debounce):
|
||||
self.calculated_status = Service.CALCULATED_PASSING_STATUS
|
||||
if self.pk:
|
||||
# This should not be necessary
|
||||
with transaction.commit_manually():
|
||||
try:
|
||||
recent_results = list(self.recent_results())
|
||||
if calculate_debounced_passing(recent_results, self.debounce):
|
||||
self.calculated_status = Service.CALCULATED_PASSING_STATUS
|
||||
else:
|
||||
self.calculated_status = Service.CALCULATED_FAILING_STATUS
|
||||
self.cached_health = serialize_recent_results(recent_results)
|
||||
transaction.commit()
|
||||
except SoftTimeLimitExceeded as e:
|
||||
# Something weird with postgres
|
||||
transaction.rollback()
|
||||
logger.error('Celery time limit exceeded for getting results for %s' % self.pk)
|
||||
self.calculated_status = Service.CALCULATED_FAILING_STATUS
|
||||
self.cached_health = '-1'
|
||||
except Exception as e:
|
||||
transaction.rollback()
|
||||
logger.error('Got exception when saving check: %s' % e)
|
||||
self.calculated_status = Service.CALCULATED_FAILING_STATUS
|
||||
self.cached_health = '-1'
|
||||
try:
|
||||
updated = StatusCheck.objects.get(pk=self.pk)
|
||||
except StatusCheck.DoesNotExist as e:
|
||||
logger.error('Cannot find myself (check %s) in the database, presumably have been deleted' % self.pk)
|
||||
return
|
||||
else:
|
||||
self.calculated_status = Service.CALCULATED_FAILING_STATUS
|
||||
self.cached_health = serialize_recent_results(recent_results)
|
||||
self.cached_health = ''
|
||||
self.calculated_status = Service.CALCULATED_PASSING_STATUS
|
||||
ret = super(StatusCheck, self).save(*args, **kwargs)
|
||||
# Update linked services
|
||||
self.update_related_services()
|
||||
self.update_related_instances()
|
||||
return ret
|
||||
|
||||
def duplicate(self, inst_set=None, serv_set=None):
|
||||
new_check = self
|
||||
new_check.pk = None
|
||||
new_check.id = None
|
||||
new_check.save()
|
||||
if inst_set is not None:
|
||||
new_check.instance_set = inst_set
|
||||
if serv_set is not None:
|
||||
new_check.service_set = serv_set
|
||||
new_check.save()
|
||||
return new_check.pk
|
||||
|
||||
def update_related_services(self):
|
||||
services = self.service_set.all()
|
||||
for service in services:
|
||||
update_service.delay(service.id)
|
||||
|
||||
def update_related_instances(self):
|
||||
instances = self.instance_set.all()
|
||||
for instance in instances:
|
||||
update_instance.delay(instance.id)
|
||||
|
||||
class ICMPStatusCheck(StatusCheck):
|
||||
|
||||
class Meta(StatusCheck.Meta):
|
||||
proxy = True
|
||||
|
||||
@property
|
||||
def check_category(self):
|
||||
return "ICMP/Ping Check"
|
||||
|
||||
def _run(self):
|
||||
result = StatusCheckResult(check=self)
|
||||
instances = self.instance_set.all()
|
||||
target = self.instance_set.get().address
|
||||
|
||||
# We need to read both STDOUT and STDERR because ping can write to both, depending on the kind of error. Thanks a lot, ping.
|
||||
ping_process = subprocess.Popen("ping -c 1 " + target, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||
response = ping_process.wait()
|
||||
|
||||
if response == 0:
|
||||
result.succeeded = True
|
||||
else:
|
||||
output = ping_process.stdout.read()
|
||||
result.succeeded = False
|
||||
result.error = output
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class GraphiteStatusCheck(StatusCheck):
|
||||
|
||||
@ -532,7 +685,6 @@ class HttpStatusCheck(StatusCheck):
|
||||
result.succeeded = True
|
||||
return result
|
||||
|
||||
|
||||
class JenkinsStatusCheck(StatusCheck):
|
||||
|
||||
class Meta(StatusCheck.Meta):
|
||||
|
@ -72,6 +72,16 @@ def update_service(service_or_id):
|
||||
service.update_status()
|
||||
|
||||
|
||||
@task(ignore_result=True)
|
||||
def update_instance(instance_or_id):
|
||||
from .models import Instance
|
||||
if not isinstance(instance_or_id, Instance):
|
||||
instance = Instance.objects.get(id=instance_or_id)
|
||||
else:
|
||||
instance = instance_or_id
|
||||
instance.update_status()
|
||||
|
||||
|
||||
@task(ignore_result=True)
|
||||
def update_shifts():
|
||||
from .models import update_shifts as _update_shifts
|
||||
|
@ -8,7 +8,7 @@ from django.contrib.auth.models import User
|
||||
from django.test.client import Client
|
||||
from cabotapp.models import (
|
||||
GraphiteStatusCheck, JenkinsStatusCheck,
|
||||
HttpStatusCheck, Service, StatusCheckResult)
|
||||
HttpStatusCheck, ICMPStatusCheck, Service, Instance, StatusCheckResult)
|
||||
from cabotapp.views import StatusCheckReportForm
|
||||
from mock import Mock, patch
|
||||
from twilio import rest
|
||||
@ -66,6 +66,7 @@ class LocalTestCase(TestCase):
|
||||
self.service = Service.objects.create(
|
||||
name='Service',
|
||||
)
|
||||
|
||||
self.service.status_checks.add(
|
||||
self.graphite_check, self.jenkins_check, self.http_check)
|
||||
# Passing is most recent
|
||||
@ -310,6 +311,22 @@ class TestWebInterface(LocalTestCase):
|
||||
# Still the same
|
||||
self.assertEqual(reloaded.hackpad_id, snippet_link)
|
||||
|
||||
def test_create_instance(self):
|
||||
instances = Instance.objects.all()
|
||||
self.assertEqual(len(instances), 0)
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
resp = self.client.post(
|
||||
reverse('create-instance'),
|
||||
data={
|
||||
'name': 'My little instance',
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
instances = Instance.objects.all()
|
||||
self.assertEqual(len(instances), 1)
|
||||
instance = instances[0]
|
||||
self.assertEqual(len(instance.status_checks.all()), 1)
|
||||
|
||||
def test_checks_report(self):
|
||||
form = StatusCheckReportForm({
|
||||
'service': self.service.id,
|
||||
|
@ -5,8 +5,8 @@ from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.conf import settings
|
||||
from models import (
|
||||
StatusCheck, GraphiteStatusCheck, JenkinsStatusCheck, HttpStatusCheck,
|
||||
StatusCheckResult, UserProfile, Service, Shift, get_duty_officers)
|
||||
StatusCheck, GraphiteStatusCheck, JenkinsStatusCheck, HttpStatusCheck, ICMPStatusCheck,
|
||||
StatusCheckResult, UserProfile, Service, Instance, Shift, get_duty_officers)
|
||||
from tasks import run_status_check as _run_status_check
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
@ -38,7 +38,7 @@ class LoginRequiredMixin(object):
|
||||
def subscriptions(request):
|
||||
""" Simple list of all checks """
|
||||
t = loader.get_template('cabotapp/subscriptions.html')
|
||||
services = Service.objects.all().order_by('alerts_enabled')
|
||||
services = Service.objects.all()
|
||||
users = User.objects.filter(is_active=True)
|
||||
c = RequestContext(request, {
|
||||
'services': services,
|
||||
@ -55,6 +55,32 @@ def run_status_check(request, pk):
|
||||
return HttpResponseRedirect(reverse('check', kwargs={'pk': pk}))
|
||||
|
||||
|
||||
def duplicate_icmp_check(request, pk):
|
||||
pc = StatusCheck.objects.get(pk=pk)
|
||||
npk = pc.duplicate()
|
||||
return HttpResponseRedirect(reverse('update-icmp-check', kwargs={'pk': npk}))
|
||||
|
||||
def duplicate_instance(request, pk):
|
||||
instance = Instance.objects.get(pk=pk)
|
||||
new_instance = instance.duplicate()
|
||||
return HttpResponseRedirect(reverse('update-instance', kwargs={'pk': new_instance}))
|
||||
|
||||
def duplicate_http_check(request, pk):
|
||||
pc = StatusCheck.objects.get(pk=pk)
|
||||
npk = pc.duplicate()
|
||||
return HttpResponseRedirect(reverse('update-http-check', kwargs={'pk': npk}))
|
||||
|
||||
def duplicate_graphite_check(request, pk):
|
||||
pc = StatusCheck.objects.get(pk=pk)
|
||||
npk = pc.duplicate()
|
||||
return HttpResponseRedirect(reverse('update-graphite-check', kwargs={'pk': npk}))
|
||||
|
||||
def duplicate_jenkins_check(request, pk):
|
||||
pc = StatusCheck.objects.get(pk=pk)
|
||||
npk = pc.duplicate()
|
||||
return HttpResponseRedirect(reverse('update-jenkins-check', kwargs={'pk': npk}))
|
||||
|
||||
|
||||
class StatusCheckResultDetailView(LoginRequiredMixin, DetailView):
|
||||
model = StatusCheckResult
|
||||
context_object_name = 'result'
|
||||
@ -90,7 +116,9 @@ base_widgets = {
|
||||
|
||||
|
||||
class StatusCheckForm(SymmetricalForm):
|
||||
symmetrical_fields = ('service_set',)
|
||||
|
||||
symmetrical_fields = ('service_set', 'instance_set')
|
||||
|
||||
service_set = forms.ModelMultipleChoiceField(
|
||||
queryset=Service.objects.all(),
|
||||
required=False,
|
||||
@ -103,6 +131,18 @@ class StatusCheckForm(SymmetricalForm):
|
||||
)
|
||||
)
|
||||
|
||||
instance_set = forms.ModelMultipleChoiceField(
|
||||
queryset=Instance.objects.all(),
|
||||
required=False,
|
||||
help_text='Link to instance(s).',
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={
|
||||
'data-rel': 'chosen',
|
||||
'style': 'width: 70%',
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class GraphiteStatusCheckForm(StatusCheckForm):
|
||||
|
||||
@ -135,6 +175,20 @@ class GraphiteStatusCheckForm(StatusCheckForm):
|
||||
})
|
||||
|
||||
|
||||
class ICMPStatusCheckForm(StatusCheckForm):
|
||||
|
||||
class Meta:
|
||||
model = ICMPStatusCheck
|
||||
fields = (
|
||||
'name',
|
||||
'frequency',
|
||||
'importance',
|
||||
'active',
|
||||
'debounce',
|
||||
)
|
||||
widgets = dict(**base_widgets)
|
||||
|
||||
|
||||
class HttpStatusCheckForm(StatusCheckForm):
|
||||
|
||||
class Meta:
|
||||
@ -196,6 +250,53 @@ class UserProfileForm(forms.ModelForm):
|
||||
exclude = ('user',)
|
||||
|
||||
|
||||
class InstanceForm(SymmetricalForm):
|
||||
|
||||
symmetrical_fields = ('service_set',)
|
||||
service_set = forms.ModelMultipleChoiceField(
|
||||
queryset=Service.objects.all(),
|
||||
required=False,
|
||||
help_text='Link to service(s).',
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={
|
||||
'data-rel': 'chosen',
|
||||
'style': 'width: 70%',
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Instance
|
||||
template_name = 'instance_form.html'
|
||||
fields = (
|
||||
'name',
|
||||
'address',
|
||||
'users_to_notify',
|
||||
'status_checks',
|
||||
'service_set',
|
||||
)
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'style': 'width: 30%;'}),
|
||||
'address': forms.TextInput(attrs={'style': 'width: 70%;'}),
|
||||
'status_checks': forms.SelectMultiple(attrs={
|
||||
'data-rel': 'chosen',
|
||||
'style': 'width: 70%',
|
||||
}),
|
||||
'service_set': forms.SelectMultiple(attrs={
|
||||
'data-rel': 'chosen',
|
||||
'style': 'width: 70%',
|
||||
}),
|
||||
'users_to_notify': forms.CheckboxSelectMultiple(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
ret = super(InstanceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['users_to_notify'].queryset = User.objects.filter(
|
||||
is_active=True)
|
||||
return ret
|
||||
|
||||
|
||||
class ServiceForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
@ -206,6 +307,7 @@ class ServiceForm(forms.ModelForm):
|
||||
'url',
|
||||
'users_to_notify',
|
||||
'status_checks',
|
||||
'instances',
|
||||
'email_alert',
|
||||
'hipchat_alert',
|
||||
'sms_alert',
|
||||
@ -220,6 +322,10 @@ class ServiceForm(forms.ModelForm):
|
||||
'data-rel': 'chosen',
|
||||
'style': 'width: 70%',
|
||||
}),
|
||||
'instances': forms.SelectMultiple(attrs={
|
||||
'data-rel': 'chosen',
|
||||
'style': 'width: 70%',
|
||||
}),
|
||||
'users_to_notify': forms.CheckboxSelectMultiple(),
|
||||
'hackpad_id': forms.TextInput(attrs={'style': 'width:30%;'}),
|
||||
}
|
||||
@ -293,20 +399,30 @@ class CheckCreateView(LoginRequiredMixin, CreateView):
|
||||
if metric:
|
||||
initial['metric'] = metric
|
||||
service_id = self.request.GET.get('service')
|
||||
instance_id = self.request.GET.get('instance')
|
||||
|
||||
if service_id:
|
||||
try:
|
||||
service = Service.objects.get(id=service_id)
|
||||
initial['service_set'] = [service]
|
||||
except Service.DoesNotExist:
|
||||
pass
|
||||
|
||||
if instance_id:
|
||||
try:
|
||||
instance = Instance.objects.get(id=instance_id)
|
||||
initial['instance_set'] = [instance]
|
||||
except Instance.DoesNotExist:
|
||||
pass
|
||||
|
||||
return initial
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.GET.get('service') != '':
|
||||
if self.request.GET.get('service'):
|
||||
return reverse('service', kwargs={'pk': self.request.GET.get('service')})
|
||||
else:
|
||||
return reverse('checks')
|
||||
|
||||
if self.request.GET.get('instance'):
|
||||
return reverse('instance', kwargs={'pk': self.request.GET.get('instance')})
|
||||
return reverse('checks')
|
||||
|
||||
|
||||
class CheckUpdateView(LoginRequiredMixin, UpdateView):
|
||||
@ -315,16 +431,22 @@ class CheckUpdateView(LoginRequiredMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse('check', kwargs={'pk': self.object.id})
|
||||
|
||||
class ICMPCheckCreateView(CheckCreateView):
|
||||
model = ICMPStatusCheck
|
||||
form_class = ICMPStatusCheckForm
|
||||
|
||||
class GraphiteCheckCreateView(CheckCreateView):
|
||||
model = GraphiteStatusCheck
|
||||
form_class = GraphiteStatusCheckForm
|
||||
|
||||
class ICMPCheckUpdateView(CheckUpdateView):
|
||||
model = ICMPStatusCheck
|
||||
form_class = ICMPStatusCheckForm
|
||||
|
||||
class GraphiteCheckUpdateView(CheckUpdateView):
|
||||
model = GraphiteStatusCheck
|
||||
form_class = GraphiteStatusCheckForm
|
||||
|
||||
class GraphiteCheckCreateView(CheckCreateView):
|
||||
model = GraphiteStatusCheck
|
||||
form_class = GraphiteStatusCheckForm
|
||||
|
||||
class HttpCheckCreateView(CheckCreateView):
|
||||
model = HttpStatusCheck
|
||||
@ -359,7 +481,7 @@ class StatusCheckListView(LoginRequiredMixin, ListView):
|
||||
context_object_name = 'checks'
|
||||
|
||||
def get_queryset(self):
|
||||
return StatusCheck.objects.all().order_by('name').prefetch_related('service_set')
|
||||
return StatusCheck.objects.all().order_by('name').prefetch_related('service_set', 'instance_set')
|
||||
|
||||
|
||||
class StatusCheckDeleteView(LoginRequiredMixin, DeleteView):
|
||||
@ -397,6 +519,15 @@ class UserProfileUpdateView(LoginRequiredMixin, UpdateView):
|
||||
return profile
|
||||
|
||||
|
||||
class InstanceListView(LoginRequiredMixin, ListView):
|
||||
|
||||
model = Instance
|
||||
context_object_name = 'instances'
|
||||
|
||||
def get_queryset(self):
|
||||
return Instance.objects.all().order_by('name').prefetch_related('status_checks')
|
||||
|
||||
|
||||
class ServiceListView(LoginRequiredMixin, ListView):
|
||||
model = Service
|
||||
context_object_name = 'services'
|
||||
@ -404,6 +535,20 @@ class ServiceListView(LoginRequiredMixin, ListView):
|
||||
def get_queryset(self):
|
||||
return Service.objects.all().order_by('name').prefetch_related('status_checks')
|
||||
|
||||
class InstanceDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Instance
|
||||
context_object_name = 'instance'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(InstanceDetailView, self).get_context_data(**kwargs)
|
||||
date_from = date.today() - relativedelta(day=1)
|
||||
context['report_form'] = StatusCheckReportForm(initial={
|
||||
'checks': self.object.status_checks.all(),
|
||||
'service': self.object,
|
||||
'date_from': date_from,
|
||||
'date_to': date_from + relativedelta(months=1) - relativedelta(days=1)
|
||||
})
|
||||
return context
|
||||
|
||||
class ServiceDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Service
|
||||
@ -421,6 +566,46 @@ class ServiceDetailView(LoginRequiredMixin, DetailView):
|
||||
return context
|
||||
|
||||
|
||||
class InstanceCreateView(LoginRequiredMixin, CreateView):
|
||||
model = Instance
|
||||
form_class = InstanceForm
|
||||
|
||||
def form_valid(self, form):
|
||||
ret = super(InstanceCreateView, self).form_valid(form)
|
||||
if self.object.status_checks.filter(polymorphic_ctype__model='icmpstatuscheck').count() == 0:
|
||||
self.generate_default_ping_check(self.object)
|
||||
return ret
|
||||
|
||||
def generate_default_ping_check(self, obj):
|
||||
pc = ICMPStatusCheck(
|
||||
name="Default Ping Check for %s" % obj.name,
|
||||
frequency=5,
|
||||
importance=Service.ERROR_STATUS,
|
||||
debounce=0,
|
||||
created_by=None,
|
||||
)
|
||||
pc.save()
|
||||
obj.status_checks.add(pc)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('instance', kwargs={'pk': self.object.id})
|
||||
|
||||
def get_initial(self):
|
||||
if self.initial:
|
||||
initial = self.initial
|
||||
else:
|
||||
initial = {}
|
||||
service_id = self.request.GET.get('service')
|
||||
|
||||
if service_id:
|
||||
try:
|
||||
service = Service.objects.get(id=service_id)
|
||||
initial['service_set'] = [service]
|
||||
except Service.DoesNotExist:
|
||||
pass
|
||||
|
||||
return initial
|
||||
|
||||
class ServiceCreateView(LoginRequiredMixin, CreateView):
|
||||
model = Service
|
||||
form_class = ServiceForm
|
||||
@ -428,6 +613,12 @@ class ServiceCreateView(LoginRequiredMixin, CreateView):
|
||||
def get_success_url(self):
|
||||
return reverse('service', kwargs={'pk': self.object.id})
|
||||
|
||||
class InstanceUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Instance
|
||||
form_class = InstanceForm
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('instance', kwargs={'pk': self.object.id})
|
||||
|
||||
class ServiceUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Service
|
||||
@ -444,6 +635,13 @@ class ServiceDeleteView(LoginRequiredMixin, DeleteView):
|
||||
template_name = 'cabotapp/service_confirm_delete.html'
|
||||
|
||||
|
||||
class InstanceDeleteView(LoginRequiredMixin, DeleteView):
|
||||
model = Instance
|
||||
success_url = reverse_lazy('instances')
|
||||
context_object_name = 'instance'
|
||||
template_name = 'cabotapp/instance_confirm_delete.html'
|
||||
|
||||
|
||||
class ShiftListView(LoginRequiredMixin, ListView):
|
||||
model = Shift
|
||||
context_object_name = 'shifts'
|
||||
|
@ -6,6 +6,8 @@ CELERY_IMPORTS = ('app.cabotapp.tasks', )
|
||||
CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler"
|
||||
CELERY_TASK_SERIALIZER = "json"
|
||||
CELERY_ACCEPT_CONTENT = ['json', 'msgpack', 'yaml']
|
||||
CELERYD_TASK_SOFT_TIME_LIMIT = 120
|
||||
CELERYD_TASK_TIME_LIMIT = 150
|
||||
|
||||
CELERYBEAT_SCHEDULE = {
|
||||
'run-all-checks': {
|
||||
|
@ -91,6 +91,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.transaction.TransactionMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'app.urls'
|
||||
|
@ -41,6 +41,9 @@
|
||||
</div>
|
||||
<div class="navbar-collapse collapse" id="navbar-main">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a href="{% url instances %}"><i class="fa fa-desktop"></i> Instances</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url services %}"><i class="fa fa-gears"></i> Services</a>
|
||||
</li>
|
||||
@ -54,15 +57,21 @@
|
||||
<li>
|
||||
<a href="{% url create-service %}"><i class="fa fa-gears"></i> Service</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url create-instance %}"><i class="fa fa-desktop"></i> Instance</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="{% url create-check %}?service={{ service.id }}" class=""><i class="glyphicon glyphicon-signal" title="Add new metric check"></i> Graphite check</a>
|
||||
<a href="{% url create-check %}?service={{ service.id }}&instance={{ instance.id }}" class=""><i class="glyphicon glyphicon-signal" title="Add new metric check"></i> Graphite check</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url create-http-check %}?service={{ service.id }}" class="" title="Add new Http check"><i class="glyphicon glyphicon-arrow-up"></i> Http check</a>
|
||||
<a href="{% url create-http-check %}?service={{ service.id }}&instance={{ instance.id }}" class="" title="Add new Http check"><i class="glyphicon glyphicon-arrow-up"></i> Http check</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url create-jenkins-check %}?service={{ service.id }}" class="" title="Add new Jenkins check"><i class="glyphicon glyphicon-ok"></i> Jenkins check</a>
|
||||
<a href="{% url create-jenkins-check %}?service={{ service.id }}&instance={{ instance.id }}" class="" title="Add new Jenkins check"><i class="glyphicon glyphicon-ok"></i> Jenkins check</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url create-icmp-check %}?service={{ service.id }}&instance={{ instance.id }}" class="" title="Add new ICMP check"><i class="glyphicon glyphicon-transfer"></i> ICMP check</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
43
app/templates/cabotapp/_instance_list.html
Normal file
43
app/templates/cabotapp/_instance_list.html
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
<table class="table bootstrap-datatable datatable">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if not instances %}
|
||||
<div class="col-xs-11 col-xs-offset-1">
|
||||
<hr></hr>
|
||||
No instances configured
|
||||
</div>
|
||||
</tr>
|
||||
{% else %}
|
||||
<th>Name</th>
|
||||
<th>Overall</th>
|
||||
<th>Active checks</th>
|
||||
<th>Disabled checks</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for instance in instances %}
|
||||
<tr class="enabled">
|
||||
<td>
|
||||
<a href="{% url instance pk=instance.id %}" title="Alerts {% if instance.alerts_enabled %}enabled{% else %}disabled{% endif %}">{{instance.name}} </a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if instance.overall_status == instance.PASSING_STATUS %}success{% else %}danger{% endif %}">{{ instance.overall_status|lower|capfirst }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if instance.active_status_checks.all.count > 0 %}{% if instance.overall_status != instance.PASSING_STATUS %}danger{% else %}success{% endif %}{% else %}default{% endif %}">{{ instance.active_status_checks.all.count }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if instance.inactive_status_checks.all.count > 0 %}warning{% else %}default{% endif %}">{{ instance.inactive_status_checks.all.count }}</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-xs" href="{% url update-instance pk=instance.id %}" role="button">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endif %}
|
||||
</table>
|
43
app/templates/cabotapp/_service_list.html
Normal file
43
app/templates/cabotapp/_service_list.html
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
<table class="table bootstrap-datatable datatable">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if not services %}
|
||||
<div class="col-xs-11 col-xs-offset-1">
|
||||
<hr></hr>
|
||||
No services configured
|
||||
</div>
|
||||
</tr>
|
||||
{% else %}
|
||||
<th>Name</th>
|
||||
<th>Overall</th>
|
||||
<th>Active checks</th>
|
||||
<th>Disabled checks</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in services %}
|
||||
<tr class="{% if service.alerts_enabled %}enabled{% else %}warning{% endif %}">
|
||||
<td>
|
||||
<a href="{% url service pk=service.id %}" title="Alerts {% if service.alerts_enabled %}enabled{% else %}disabled{% endif %}">{{service.name}} </a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if not service.alerts_enabled %}warning{% else %}{% if service.overall_status == service.PASSING_STATUS %}success{% else %}danger{% endif %}{% endif %}">{% if service.alerts_enabled %}{{ service.overall_status|lower|capfirst }}{% else %}Disabled{% endif %}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if service.active_status_checks.all.count > 0 %}{% if service.overall_status != service.PASSING_STATUS %}danger{% else %}success{% endif %}{% else %}default{% endif %}">{{ service.active_status_checks.all.count }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if service.inactive_status_checks.all.count > 0 %}warning{% else %}default{% endif %}">{{ service.inactive_status_checks.all.count }}</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-xs" href="{% url update-service pk=service.id %}" role="button">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endif %}
|
||||
</table>
|
@ -2,18 +2,21 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h3><i class="{% if checks_type == "All" %}fa fa-cog{% else %}glyphicon glyphicon-{% if checks_type == "Http" %}arrow-up{% elif checks_type == "Jenkins" %}ok{% else %}signal{% endif %}{% endif %}"></i></h3></div>
|
||||
<div class="col-xs-1"><h3><i class="{% if checks_type == "All" %}fa fa-cog{% else %}glyphicon glyphicon-{% if checks_type == "Http" %}arrow-up{% elif checks_type == "Jenkins" %}ok{% elif checks_type == "ICMP" %}transfer{% else %}signal{% endif %}{% endif %}"></i></h3></div>
|
||||
<div class="col-xs-8"><h3>{{ checks_type }} checks</h3></div>
|
||||
<div class="col-xs-3 text-right">
|
||||
<h3>
|
||||
{% if checks_type == "All" or checks_type == "Graphite" %}
|
||||
<a href="{% url create-check %}?service={{ service.id }}" class=""><i class="glyphicon glyphicon-plus" title="Add new metric check"></i><i class="glyphicon glyphicon-signal" title="Add new metric check"></i></a>
|
||||
<a href="{% url create-check %}?instance={{ instance.id }}&service={{ service.id }}" class=""><i class="glyphicon glyphicon-plus" title="Add new metric check"></i><i class="glyphicon glyphicon-signal" title="Add new metric check"></i></a>
|
||||
{% endif %}
|
||||
{% if checks_type == "All" or checks_type == "Http" %}
|
||||
<a href="{% url create-http-check %}?service={{ service.id }}" class="" title="Add new Http check"><i class="glyphicon glyphicon-plus"></i><i class="glyphicon glyphicon-arrow-up"></i></a>
|
||||
<a href="{% url create-http-check %}?instance={{ instance.id }}&service={{ service.id }}" class="" title="Add new Http check"><i class="glyphicon glyphicon-plus"></i><i class="glyphicon glyphicon-arrow-up"></i></a>
|
||||
{% endif %}
|
||||
{% if checks_type == "All" or checks_type == "Jenkins" %}
|
||||
<a href="{% url create-jenkins-check %}?service={{ service.id }}" class="" title="Add new Jenkins check"><i class="glyphicon glyphicon-plus"></i><i class="glyphicon glyphicon-ok"></i></a>
|
||||
<a href="{% url create-jenkins-check %}?instance={{ instance.id }}&service={{ service.id }}" class="" title="Add new Jenkins check"><i class="glyphicon glyphicon-plus"></i><i class="glyphicon glyphicon-ok"></i></a>
|
||||
{% endif %}
|
||||
{% if checks_type == "All" or checks_type == "ICMP" %}
|
||||
<a href="{% url create-icmp-check %}?instance={{ instance.id }}&service={{ service.id }}" class="" title="Add new ICMP check"><i class="glyphicon glyphicon-plus"></i><i class="glyphicon glyphicon-transfer"></i></a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
@ -37,6 +40,7 @@
|
||||
<th>Test description</th>
|
||||
<th>Importance</th>
|
||||
<th>Service(s)</th>
|
||||
<th>Instance(s)</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -62,11 +66,11 @@
|
||||
</td>
|
||||
{% if checks_type == "All" %}
|
||||
<td class="text-center">
|
||||
<i class="glyphicon glyphicon-{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}signal{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}arrow-up{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}ok{% endif %}"></i>
|
||||
<i class="glyphicon glyphicon-{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}signal{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}arrow-up{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}ok{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}transfer{% endif %}"></i>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td title="">
|
||||
{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{{ check.metric|truncatechars:70 }} {{ check.check_type }} {{ check.value }}{% if check.expected_num_hosts %} (from {{ check.expected_num_hosts }} hosts){% endif %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}Status code {{ check.status_code }} from {{ check.endpoint }}{% if check.text_match %}; match text /{{ check.text_match }}/{% endif %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}Monitor job {{ check.name }}{% if check.max_queued_build_time %}; check no build waiting for >{{ check.max_queued_build_time }} minutes{% endif %}{% endif %}
|
||||
{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{{ check.metric|truncatechars:70 }} {{ check.check_type }} {{ check.value }}{% if check.expected_num_hosts %} (from {{ check.expected_num_hosts }} hosts){% endif %}{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}ICMP Reply from {{ check.instance_set.all.0.address }}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}Status code {{ check.status_code }} from {{ check.endpoint }}{% if check.text_match %}; match text /{{ check.text_match }}/{% endif %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}Monitor job {{ check.name }}{% if check.max_queued_build_time %}; check no build waiting for >{{ check.max_queued_build_time }} minutes{% endif %}{% endif %}
|
||||
</td>
|
||||
<td>{{ check.get_importance_display }}</td>
|
||||
<td>
|
||||
@ -81,10 +85,25 @@
|
||||
<span class="label label-warning">No service</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for instance in check.instance_set.all %}
|
||||
<a href="{% url instance pk=instance.id %}">{{ instance.name }}</a>
|
||||
{% if forloop.last %}
|
||||
{% else %}
|
||||
/
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not check.instance_set.all %}
|
||||
<span class="label label-warning">No instance</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-xs" href="{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{% url update-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}{% url update-http-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}{% url update-jenkins-check pk=check.id %}{% endif %}">
|
||||
<a class="btn btn-xs" href="{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{% url update-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}{% url update-http-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}{% url update-icmp-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}{% url update-jenkins-check pk=check.id %}{% endif %}">
|
||||
<i class="glyphicon glyphicon-edit"></i><span class="break"></span>
|
||||
</a>
|
||||
<a class="btn btn-xs" href="{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{% url duplicate-graphite-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}{% url duplicate-http-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}{% url duplicate-icmp-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}{% url duplicate-jenkins-check pk=check.id %}{% endif %}">
|
||||
<i class="fa fa-copy"></i><span class="break"></span>
|
||||
</a>
|
||||
<a class="btn btn-xs" href="{% url run-check pk=check.id %}">
|
||||
<i class="glyphicon glyphicon-refresh"></i><span class="break"></span>
|
||||
</a>
|
||||
|
18
app/templates/cabotapp/instance_confirm_delete.html
Normal file
18
app/templates/cabotapp/instance_confirm_delete.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Delete service</h1>
|
||||
<form action="." method="post">{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger">Delete {{ object }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% load compress %}
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
{% compress js %}
|
||||
<script type="text/coffeescript">
|
||||
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
209
app/templates/cabotapp/instance_detail.html
Normal file
209
app/templates/cabotapp/instance_detail.html
Normal file
@ -0,0 +1,209 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ block.super }} - {{ instance.name }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h2><i class="fa fa-desktop"></i></h2></div>
|
||||
<div class="col-xs-5"><h2><span class="break"></span>{{ instance.name }}</h2></div>
|
||||
<div class="col-xs-4 text-right"><h2><span class="label label-{% if instance.overall_status == instance.PASSING_STATUS %}success{% else %}danger{% endif %}">{{ instance.overall_status|lower|capfirst }}</span> </h2></div>
|
||||
<div class="col-xs-2 text-right"><h2><a class = "" href="{% url duplicate-instance instance.id %}"><i class="fa fa-copy"></i> </a><a class = "" href="{% url update-instance instance.id %}"><i class="glyphicon glyphicon-edit"></i> </a></h2></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-xs-12">
|
||||
<div class="col-xs-1"><h3><i class="fa fa-cog"></i></h3></div>
|
||||
<div class="col-xs-11"><h3>Configuration</h3></div>
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h5><i class="glyphicon glyphicon-home"></i></h5></div>
|
||||
<div class="col-xs-3"><h5><span class="break"></span>Address</h5></div>
|
||||
<div class="col-xs-8"><h5>{{ instance.address|urlize|default:"None configured" }}</h5></div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h5><i class="glyphicon glyphicon-user"></i></h5></div>
|
||||
<div class="col-xs-3"><h5><span class="break"></span>Users watching</h5></div>
|
||||
<div class="col-xs-8"><h5>
|
||||
{% if not instance.users_to_notify.all %}
|
||||
No users subscribed
|
||||
{% else %}
|
||||
{{ instance.users_to_notify.all|join:", " }}
|
||||
{% endif %}
|
||||
</h5></div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h5><i class="fa fa-exclamation-triangle"></i></h5></div>
|
||||
<div class="col-xs-3"><h5><span class="break"></span>Alert types</h5></div>
|
||||
<div class="col-xs-8">
|
||||
<h5>
|
||||
{% if instance.email_alert %}<i class="fa fa-envelope"></i> Email{% endif %}
|
||||
{% if instance.hipchat_alert %}<i class="fa fa-comment"></i> Hipchat{% endif %}
|
||||
{% if instance.sms_alert %}<i class="fa fa-mobile"></i> SMS{% endif %}
|
||||
{% if instance.telephone_alert %}<i class="fa fa-phone"></i> Telephone{% endif %}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="col-xs-1"><h3><i class="fa fa-bar-chart-o"></i></h3></div>
|
||||
<div class="col-xs-11"><h3>Status (24 hours)</h3></div>
|
||||
<div class="col-xs-12">
|
||||
<div id="graph" style="height: 150px; margin: 1 0px;"></div>
|
||||
<div id="timeline"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h3><i class="fa fa-gears"></i></h3></div>
|
||||
<div class="col-xs-8"><h3>Services</h3></div>
|
||||
</div>
|
||||
{% include 'cabotapp/_service_list.html' with services=instance.service_set.all %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
{% include 'cabotapp/_statuscheck_list.html' with checks=instance.graphite_status_checks.all instance=instance checks_type="Graphite" %}
|
||||
|
||||
<hr>
|
||||
|
||||
{% include 'cabotapp/_statuscheck_list.html' with checks=instance.http_status_checks.all instance=instance checks_type="Http" %}
|
||||
|
||||
<hr>
|
||||
|
||||
{% include 'cabotapp/_statuscheck_list.html' with checks=instance.jenkins_status_checks.all instance=instance checks_type="Jenkins" %}
|
||||
|
||||
<hr>
|
||||
|
||||
{% include 'cabotapp/_statuscheck_list.html' with checks=instance.icmp_status_checks.all instance=instance checks_type="ICMP" %}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h3><i class="fa fa-table"></i></h3></div>
|
||||
<div class="col-xs-11">
|
||||
<h3>Status check report</h3>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<form action="{% url checks-report %}" method="get">
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12">
|
||||
{{ report_form.instance }}
|
||||
<label class="col-xs-2 control-label">{{ report_form.checks.label_tag }}</label>
|
||||
<div class="col-xs-10">{{ report_form.checks }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12">
|
||||
<label class="col-xs-2 control-label">{{ report_form.date_from.label_tag }}</label>
|
||||
<div class="col-xs-10">{{ report_form.date_from }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12">
|
||||
<label class="col-xs-2 control-label">{{ report_form.date_to.label_tag }}</label>
|
||||
<div class="col-xs-10">{{ report_form.date_to }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div class="form-group">
|
||||
<div class="col-xs-6 col-xs-offset-2">
|
||||
<button type="submit" class="btn btn-primary">Get report</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block js %}
|
||||
{% load compress %}
|
||||
{% load jsonify %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript">
|
||||
window.INSTANCE_HISTORY = {{ instance.recent_snapshots|jsonify }}
|
||||
</script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}arachnys/js/d3.js"></script>
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}arachnys/js/rickshaw.js"></script>
|
||||
<script type="text/coffeescript">
|
||||
|
||||
$(document).ready ->
|
||||
data = window.INSTANCE_HISTORY
|
||||
labels = {
|
||||
num_checks_active: 'blue'
|
||||
num_checks_failing: '#f00'
|
||||
}
|
||||
processedData = formatDataForRickshaw data, labels
|
||||
drawRickshaw processedData.series, labels, processedData.events
|
||||
|
||||
formatDataForRickshaw = (data, labels) ->
|
||||
series = {}
|
||||
events = []
|
||||
for label, color of labels
|
||||
series[label] = {
|
||||
color: color
|
||||
name: label
|
||||
data: []
|
||||
}
|
||||
for slice in data
|
||||
if slice.did_send_alert
|
||||
events.push {time: slice.time, message: 'Sent alert'}
|
||||
for label, color of labels
|
||||
series[label].data.push
|
||||
x: slice.time
|
||||
y: slice[label]
|
||||
ret = []
|
||||
for line, val of series
|
||||
ret.push val
|
||||
return {
|
||||
series: ret
|
||||
events: events
|
||||
}
|
||||
|
||||
drawRickshaw = (data, labels, events = []) ->
|
||||
rickshawLine = new Rickshaw.Graph
|
||||
renderer: 'line'
|
||||
element: document.querySelector('#graph')
|
||||
series: data
|
||||
height: 140
|
||||
rickshawLine.render()
|
||||
|
||||
hoverDetail = new Rickshaw.Graph.HoverDetail({graph: rickshawLine})
|
||||
xAxis = new Rickshaw.Graph.Axis.Time({graph: rickshawLine})
|
||||
xAxis.render()
|
||||
yAxis = new Rickshaw.Graph.Axis.Y({graph: rickshawLine})
|
||||
yAxis.render()
|
||||
|
||||
window.annotator = annotator = new Rickshaw.Graph.Annotate({
|
||||
graph: rickshawLine
|
||||
element: document.getElementById('timeline')
|
||||
})
|
||||
for evt in events
|
||||
annotator.add evt.time, evt.message
|
||||
annotator.update()
|
||||
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
$(':input.datepicker').datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
buttonImage: '{{ STATIC_URL }}theme/img/calendar.gif',
|
||||
buttonImageOnly: true,
|
||||
showOn: 'button'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% endblock js %}
|
36
app/templates/cabotapp/instance_form.html
Normal file
36
app/templates/cabotapp/instance_form.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-10 col-xs-offset-2">
|
||||
<h2>{% if form.instance.id %}Edit instance{% else %}New instance{% endif %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="form-horizontal" action="" method="post" role="form">
|
||||
{% include "cabotapp/_base_form.html" %}
|
||||
<div class="col-xs-12">
|
||||
<div class="form-group">
|
||||
<div class="col-xs-6 col-xs-offset-2">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<a href="{% url dashboard %}" class="btn">Back to dashboard</a>
|
||||
</div>
|
||||
{% if form.instance.id %}
|
||||
<div class="col-xs-4">
|
||||
<a class="btn btn-danger" href="{% url delete-instance form.instance.id %}">Delete instance</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% load compress %}
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
{% compress js %}
|
||||
<script type="text/coffeescript">
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
26
app/templates/cabotapp/instance_list.html
Normal file
26
app/templates/cabotapp/instance_list.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row sortable ui-sortable">
|
||||
<div class="col-xs-12" data-original-title="">
|
||||
<div class="col-xs-1"><h2><i class="fa fa-desktop"></i></h2></div>
|
||||
<div class="col-xs-10"><h2><span class="break"></span>Instances</h2></div>
|
||||
<div class="col-xs-1 text-right">
|
||||
<h2><a href="{% url create-instance %}" title="New instance"><i class="glyphicon glyphicon-plus"></i></a></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
{% include 'cabotapp/_instance_list.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
{% block js %}
|
||||
{% load compress %}
|
||||
{{ block.super }}
|
||||
{% compress js %}
|
||||
<script type="text/coffeescript">
|
||||
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% endblock js %}
|
@ -55,7 +55,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h3><i class="fa fa-desktop"></i></h3></div>
|
||||
<div class="col-xs-8"><h3>Instances</h3></div>
|
||||
<div class="col-xs-3 text-right">
|
||||
<h3>
|
||||
<a href="{% url create-instance %}?service={{ service.id }}" class=""><i class="glyphicon glyphicon-plus" title="Add new instance for this service"></i><i class="fa fa-desktop" title=""></i></a>
|
||||
</h3>
|
||||
</div>
|
||||
{% include 'cabotapp/_instance_list.html' with instances=service.instances.all %}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
{% include 'cabotapp/_statuscheck_list.html' with checks=service.graphite_status_checks.all service=service checks_type="Graphite" %}
|
||||
|
@ -10,44 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
{% if not services %}
|
||||
No services
|
||||
{% else %}
|
||||
<table class="table bootstrap-datatable datatable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Overall</th>
|
||||
<th>Active checks</th>
|
||||
<th>Disabled checks</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in services %}
|
||||
<tr class="{% if service.alerts_enabled %}enabled{% else %}warning{% endif %}">
|
||||
<td>
|
||||
<a href="{% url service pk=service.id %}" title="Alerts {% if service.alerts_enabled %}enabled{% else %}disabled{% endif %}">{{service.name}} </a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if not service.alerts_enabled %}warning{% else %}{% if service.overall_status == service.PASSING_STATUS %}success{% else %}danger{% endif %}{% endif %}">{% if service.alerts_enabled %}{{ service.overall_status|lower|capfirst }}{% else %}Disabled{% endif %}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if service.active_status_checks.all.count > 0 %}{% if service.overall_status != service.PASSING_STATUS %}danger{% else %}success{% endif %}{% else %}default{% endif %}">{{ service.active_status_checks.all.count }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{% if service.inactive_status_checks.all.count > 0 %}warning{% else %}default{% endif %}">{{ service.inactive_status_checks.all.count }}</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-xs" href="{% url update-service pk=service.id %}" role="button">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% include 'cabotapp/_service_list.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
@ -6,14 +6,16 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-1"><h2><i class="glyphicon glyphicon-{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}signal{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}arrow-up{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}ok{% endif %}"></i></h2></div>
|
||||
<div class="col-xs-1"><h2><i class="glyphicon glyphicon-{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}signal{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}arrow-up{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}ok{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}transfer{% endif %}"></i></h2></div>
|
||||
<div class="col-xs-5"><h2>{{ check.name }}</h2></div>
|
||||
<div class="col-xs-4 text-right"><h2><span class="label label-{% if check.calculated_status == 'passing' %}success{% else %}danger{% endif %}">{{ check.calculated_status|capfirst }}</span></h2></div>
|
||||
<div class="col-xs-2 text-right"><h2>
|
||||
{% if check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}
|
||||
<a href="{% jenkins_human_url check.name %}" class=""><i class="glyphicon glyphicon-link"></i></a>
|
||||
{% endif %}
|
||||
<a href="{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{% url update-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}{% url update-http-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}{% url update-jenkins-check pk=check.id %}{% endif %}" class=""><i class="glyphicon glyphicon-edit"></i></a> <a href="{% url run-check pk=check.id %}"><i class="glyphicon glyphicon-refresh"></i></a>
|
||||
<a href="{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{% url update-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}{% url update-http-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}{% url update-jenkins-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}{% url update-icmp-check pk=check.id %}{% endif %}" class=""><i class="glyphicon glyphicon-edit"></i>
|
||||
<a href="{% if check.polymorphic_ctype.model == 'graphitestatuscheck' %}{% url duplicate-graphite-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'httpstatuscheck' %}{% url duplicate-http-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'jenkinsstatuscheck' %}{% url duplicate-jenkins-check pk=check.id %}{% elif check.polymorphic_ctype.model == 'icmpstatuscheck' %}{% url duplicate-icmp-check pk=check.id %}{% endif %}" class=""><i class="fa fa-copy"></i>
|
||||
</a> <a href="{% url run-check pk=check.id %}"><i class="glyphicon glyphicon-refresh"></i></a>
|
||||
</h2></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,7 +51,7 @@
|
||||
</td>
|
||||
<td>{{ result.time_complete }}</td>
|
||||
<td>{{ result.took }}</td>
|
||||
<td>{{ result.error|default:"" }}</td>
|
||||
<td>{% autoescape off %}{{ result.error|default:"" }}{% endautoescape %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
37
app/urls.py
37
app/urls.py
@ -1,12 +1,15 @@
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
from cabotapp.views import (
|
||||
run_status_check, graphite_api_data, twiml_callback, checks_run_recently,
|
||||
duplicate_icmp_check, duplicate_graphite_check, duplicate_http_check, duplicate_jenkins_check, duplicate_instance,
|
||||
GraphiteCheckCreateView, GraphiteCheckUpdateView,
|
||||
HttpCheckCreateView, HttpCheckUpdateView,
|
||||
ICMPCheckCreateView, ICMPCheckUpdateView,
|
||||
JenkinsCheckCreateView, JenkinsCheckUpdateView,
|
||||
StatusCheckDeleteView, StatusCheckListView, StatusCheckDetailView,
|
||||
StatusCheckResultDetailView, StatusCheckReportView)
|
||||
from cabotapp.views import (ServiceListView, ServiceDetailView,
|
||||
from cabotapp.views import (InstanceListView, InstanceDetailView,
|
||||
InstanceUpdateView, InstanceCreateView, InstanceDeleteView, ServiceListView, ServiceDetailView,
|
||||
ServiceUpdateView, ServiceCreateView, ServiceDeleteView,
|
||||
UserProfileUpdateView, ShiftListView, subscriptions)
|
||||
from django.contrib import admin
|
||||
@ -43,6 +46,21 @@ urlpatterns = patterns('',
|
||||
url(r'^service/(?P<pk>\d+)/',
|
||||
view=ServiceDetailView.as_view(), name='service'),
|
||||
|
||||
url(r'^instances/', view=InstanceListView.as_view(),
|
||||
name='instances'),
|
||||
url(r'^instance/create/', view=InstanceCreateView.as_view(),
|
||||
name='create-instance'),
|
||||
url(r'^instance/update/(?P<pk>\d+)/',
|
||||
view=InstanceUpdateView.as_view(
|
||||
), name='update-instance'),
|
||||
url(r'^instance/duplicate/(?P<pk>\d+)/',
|
||||
view=duplicate_instance, name='duplicate-instance'),
|
||||
url(r'^instance/delete/(?P<pk>\d+)/',
|
||||
view=InstanceDeleteView.as_view(
|
||||
), name='delete-instance'),
|
||||
url(r'^instance/(?P<pk>\d+)/',
|
||||
view=InstanceDetailView.as_view(), name='instance'),
|
||||
|
||||
url(r'^checks/$', view=StatusCheckListView.as_view(),
|
||||
name='checks'),
|
||||
url(r'^check/run/(?P<pk>\d+)/',
|
||||
@ -55,22 +73,39 @@ urlpatterns = patterns('',
|
||||
url(r'^checks/report/$',
|
||||
view=StatusCheckReportView.as_view(), name='checks-report'),
|
||||
|
||||
|
||||
url(r'^icmpcheck/create/', view=ICMPCheckCreateView.as_view(),
|
||||
name='create-icmp-check'),
|
||||
url(r'^icmpcheck/update/(?P<pk>\d+)/',
|
||||
view=ICMPCheckUpdateView.as_view(
|
||||
), name='update-icmp-check'),
|
||||
url(r'^icmpcheck/duplicate/(?P<pk>\d+)/',
|
||||
view=duplicate_icmp_check, name='duplicate-icmp-check'),
|
||||
|
||||
url(r'^graphitecheck/create/',
|
||||
view=GraphiteCheckCreateView.as_view(
|
||||
), name='create-check'),
|
||||
url(r'^graphitecheck/update/(?P<pk>\d+)/',
|
||||
view=GraphiteCheckUpdateView.as_view(
|
||||
), name='update-check'),
|
||||
url(r'^graphitecheck/duplicate/(?P<pk>\d+)/',
|
||||
view=duplicate_graphite_check, name='duplicate-graphite-check'),
|
||||
|
||||
url(r'^httpcheck/create/', view=HttpCheckCreateView.as_view(),
|
||||
name='create-http-check'),
|
||||
url(r'^httpcheck/update/(?P<pk>\d+)/',
|
||||
view=HttpCheckUpdateView.as_view(
|
||||
), name='update-http-check'),
|
||||
url(r'^httpcheck/duplicate/(?P<pk>\d+)/',
|
||||
view=duplicate_http_check, name='duplicate-http-check'),
|
||||
|
||||
url(r'^jenkins_check/create/', view=JenkinsCheckCreateView.as_view(),
|
||||
name='create-jenkins-check'),
|
||||
url(r'^jenkins_check/update/(?P<pk>\d+)/',
|
||||
view=JenkinsCheckUpdateView.as_view(
|
||||
), name='update-jenkins-check'),
|
||||
url(r'^jenkins_check/duplicate/(?P<pk>\d+)/',
|
||||
view=duplicate_jenkins_check, name='duplicate-jenkins-check'),
|
||||
|
||||
url(r'^result/(?P<service_id>\d+)/twiml_callback/',
|
||||
view=twiml_callback, name='twiml-callback'),
|
||||
|
@ -1,4 +1,4 @@
|
||||
Django==1.4.10
|
||||
Django==1.4.13
|
||||
PyJWT==0.1.2
|
||||
South==0.7.6
|
||||
amqp==1.3.3
|
||||
|
Loading…
x
Reference in New Issue
Block a user