This commit is contained in:
Iliyan Angelov
2025-09-19 11:58:53 +03:00
parent 306b20e24a
commit 6b247e5b9f
11423 changed files with 1500615 additions and 778 deletions

View File

@@ -0,0 +1 @@
# Management commands for SLA & On-Call Management

View File

@@ -0,0 +1 @@
# Management commands

View File

@@ -0,0 +1,395 @@
"""
Management command to set up SLA & On-Call Management with default configurations
"""
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from datetime import time, timedelta
from django.utils import timezone
from sla_oncall.models import (
BusinessHours,
SLADefinition,
EscalationPolicy,
OnCallRotation,
OnCallAssignment,
NotificationTemplate,
)
User = get_user_model()
class Command(BaseCommand):
help = 'Set up SLA & On-Call Management with default configurations'
def add_arguments(self, parser):
parser.add_argument(
'--force',
action='store_true',
help='Force recreation of existing configurations',
)
def handle(self, *args, **options):
self.stdout.write('Setting up SLA & On-Call Management...')
force = options['force']
# Create default business hours
self.create_default_business_hours(force)
# Create default SLA definitions
self.create_default_sla_definitions(force)
# Create default escalation policies
self.create_default_escalation_policies(force)
# Create default notification templates
self.create_default_notification_templates(force)
# Create sample on-call rotation (if users exist)
self.create_sample_oncall_rotation(force)
self.stdout.write(
self.style.SUCCESS('Successfully set up SLA & On-Call Management!')
)
def create_default_business_hours(self, force):
"""Create default business hours configurations"""
self.stdout.write('Creating default business hours...')
default_hours, created = BusinessHours.objects.get_or_create(
name='Standard Business Hours',
defaults={
'description': 'Standard 9-5 business hours, Monday to Friday',
'timezone': 'UTC',
'weekday_start': time(9, 0),
'weekday_end': time(17, 0),
'weekend_start': time(10, 0),
'weekend_end': time(16, 0),
'is_active': True,
'is_default': True,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {default_hours.name}')
# 24/7 business hours
twenty_four_seven, created = BusinessHours.objects.get_or_create(
name='24/7 Operations',
defaults={
'description': '24/7 operations - always business hours',
'timezone': 'UTC',
'weekday_start': time(0, 0),
'weekday_end': time(23, 59),
'weekend_start': time(0, 0),
'weekend_end': time(23, 59),
'is_active': True,
'is_default': False,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {twenty_four_seven.name}')
def create_default_sla_definitions(self, force):
"""Create default SLA definitions"""
self.stdout.write('Creating default SLA definitions...')
# Critical incidents SLA
critical_sla, created = SLADefinition.objects.get_or_create(
name='Critical Incident Response',
defaults={
'description': 'SLA for critical and emergency incidents',
'sla_type': 'RESPONSE_TIME',
'incident_severities': ['CRITICAL', 'EMERGENCY'],
'incident_priorities': ['P1'],
'target_duration_minutes': 15,
'business_hours_only': False,
'escalation_enabled': True,
'escalation_threshold_percent': 75.0,
'is_active': True,
'is_default': False,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {critical_sla.name}')
# High priority incidents SLA
high_sla, created = SLADefinition.objects.get_or_create(
name='High Priority Response',
defaults={
'description': 'SLA for high priority incidents',
'sla_type': 'RESPONSE_TIME',
'incident_severities': ['HIGH'],
'incident_priorities': ['P2'],
'target_duration_minutes': 30,
'business_hours_only': False,
'escalation_enabled': True,
'escalation_threshold_percent': 80.0,
'is_active': True,
'is_default': False,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {high_sla.name}')
# Medium priority incidents SLA
medium_sla, created = SLADefinition.objects.get_or_create(
name='Medium Priority Response',
defaults={
'description': 'SLA for medium priority incidents during business hours',
'sla_type': 'RESPONSE_TIME',
'incident_severities': ['MEDIUM'],
'incident_priorities': ['P3'],
'target_duration_minutes': 120,
'business_hours_only': True,
'escalation_enabled': True,
'escalation_threshold_percent': 85.0,
'is_active': True,
'is_default': False,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {medium_sla.name}')
# Resolution time SLA
resolution_sla, created = SLADefinition.objects.get_or_create(
name='Critical Incident Resolution',
defaults={
'description': 'SLA for resolving critical incidents',
'sla_type': 'RESOLUTION_TIME',
'incident_severities': ['CRITICAL', 'EMERGENCY'],
'incident_priorities': ['P1'],
'target_duration_minutes': 240, # 4 hours
'business_hours_only': False,
'escalation_enabled': True,
'escalation_threshold_percent': 90.0,
'is_active': True,
'is_default': False,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {resolution_sla.name}')
def create_default_escalation_policies(self, force):
"""Create default escalation policies"""
self.stdout.write('Creating default escalation policies...')
# Critical escalation policy
critical_escalation, created = EscalationPolicy.objects.get_or_create(
name='Critical Incident Escalation',
defaults={
'description': 'Escalation policy for critical and emergency incidents',
'escalation_type': 'TIME_BASED',
'trigger_condition': 'SLA_THRESHOLD',
'incident_severities': ['CRITICAL', 'EMERGENCY'],
'trigger_delay_minutes': 0,
'escalation_steps': [
{
'level': 1,
'delay_minutes': 5,
'actions': ['notify_oncall', 'notify_manager'],
'channels': ['email', 'sms']
},
{
'level': 2,
'delay_minutes': 15,
'actions': ['notify_director', 'page_oncall'],
'channels': ['email', 'sms', 'phone']
},
{
'level': 3,
'delay_minutes': 30,
'actions': ['notify_executive', 'escalate_to_vendor'],
'channels': ['email', 'phone', 'webhook']
}
],
'notification_channels': ['email', 'sms', 'phone'],
'is_active': True,
'is_default': False,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {critical_escalation.name}')
# Standard escalation policy
standard_escalation, created = EscalationPolicy.objects.get_or_create(
name='Standard Escalation',
defaults={
'description': 'Standard escalation policy for most incidents',
'escalation_type': 'TIME_BASED',
'trigger_condition': 'SLA_THRESHOLD',
'incident_severities': ['LOW', 'MEDIUM', 'HIGH'],
'trigger_delay_minutes': 5,
'escalation_steps': [
{
'level': 1,
'delay_minutes': 15,
'actions': ['notify_oncall'],
'channels': ['email']
},
{
'level': 2,
'delay_minutes': 30,
'actions': ['notify_manager'],
'channels': ['email', 'sms']
}
],
'notification_channels': ['email', 'sms'],
'is_active': True,
'is_default': True,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {standard_escalation.name}')
def create_default_notification_templates(self, force):
"""Create default notification templates"""
self.stdout.write('Creating default notification templates...')
# Email escalation template
email_escalation, created = NotificationTemplate.objects.get_or_create(
name='Email Escalation Alert',
template_type='ESCALATION',
channel_type='EMAIL',
defaults={
'subject_template': 'URGENT: Incident #{incident_id} Escalated - {incident_title}',
'body_template': '''
Incident #{incident_id} has been escalated to Level {escalation_level}.
Details:
- Title: {incident_title}
- Severity: {incident_severity}
- Status: {incident_status}
- Created: {incident_created_at}
- SLA Target: {sla_target_time}
- Current On-Call: {current_oncall}
Please respond immediately.
View incident: {incident_url}
''',
'variables': [
'incident_id', 'incident_title', 'incident_severity',
'incident_status', 'incident_created_at', 'sla_target_time',
'current_oncall', 'incident_url', 'escalation_level'
],
'is_active': True,
'is_default': True,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {email_escalation.name}')
# SMS escalation template
sms_escalation, created = NotificationTemplate.objects.get_or_create(
name='SMS Escalation Alert',
template_type='ESCALATION',
channel_type='SMS',
defaults={
'subject_template': '',
'body_template': 'URGENT: Incident #{incident_id} escalated to L{escalation_level}. {incident_title}. Respond now.',
'variables': ['incident_id', 'incident_title', 'escalation_level'],
'is_active': True,
'is_default': True,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {sms_escalation.name}')
# SLA breach template
sla_breach, created = NotificationTemplate.objects.get_or_create(
name='SLA Breach Alert',
template_type='SLA_BREACH',
channel_type='EMAIL',
defaults={
'subject_template': 'SLA BREACH: Incident #{incident_id} - {incident_title}',
'body_template': '''
SLA BREACH ALERT
The SLA for incident #{incident_id} has been breached.
Details:
- Incident: {incident_title}
- SLA Type: {sla_type}
- Target Time: {sla_target_time}
- Breach Time: {breach_time}
- Breach Duration: {breach_duration}
Immediate action required.
''',
'variables': [
'incident_id', 'incident_title', 'sla_type',
'sla_target_time', 'breach_time', 'breach_duration'
],
'is_active': True,
'is_default': True,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {sla_breach.name}')
def create_sample_oncall_rotation(self, force):
"""Create a sample on-call rotation if users exist"""
self.stdout.write('Creating sample on-call rotation...')
# Check if we have any users
if not User.objects.exists():
self.stdout.write(' ⚠ No users found. Skipping on-call rotation creation.')
return
# Get first user as admin
admin_user = User.objects.first()
# Create sample rotation
sample_rotation, created = OnCallRotation.objects.get_or_create(
name='Primary On-Call Rotation',
defaults={
'description': 'Primary on-call rotation for incident response',
'rotation_type': 'WEEKLY',
'status': 'ACTIVE',
'team_name': 'Incident Response Team',
'team_description': 'Primary team responsible for incident response',
'schedule_config': {
'rotation_length_days': 7,
'handoff_time': '09:00',
'timezone': 'UTC'
},
'timezone': 'UTC',
'external_system': 'INTERNAL',
'created_by': admin_user,
}
)
if created or force:
self.stdout.write(f' ✓ Created/Updated: {sample_rotation.name}')
# Create a sample assignment for the next week
if User.objects.count() >= 2:
users = list(User.objects.all()[:2])
start_time = timezone.now()
end_time = start_time + timedelta(days=7)
assignment, created = OnCallAssignment.objects.get_or_create(
rotation=sample_rotation,
user=users[0],
start_time=start_time,
end_time=end_time,
defaults={
'status': 'ACTIVE',
}
)
if created:
self.stdout.write(f' ✓ Created assignment for {users[0].username}')
else:
self.stdout.write(f' ⚠ Rotation already exists: {sample_rotation.name}')