""" Automation Commands Service for ChatOps Integration Handles execution of automation commands via chat interface """ from django.utils import timezone from django.contrib.auth import get_user_model from typing import Dict, Any, Optional, List from ..models import ChatCommand, WarRoomMessage from automation_orchestration.models import Runbook, RunbookExecution, AutoRemediation, AutoRemediationExecution from incident_intelligence.models import Incident User = get_user_model() class AutomationCommandService: """Service for handling automation commands in chat""" @staticmethod def execute_runbook_command(chat_command: ChatCommand, runbook_name: str, user: User) -> Dict[str, Any]: """Execute a runbook via chat command""" try: incident = chat_command.message.war_room.incident # Find the runbook runbook = Runbook.objects.filter( name__icontains=runbook_name, is_active=True ).first() if not runbook: return { 'error': f'Runbook "{runbook_name}" not found or inactive', 'suggestions': AutomationCommandService._get_runbook_suggestions(runbook_name) } # Check if runbook applies to this incident if not AutomationCommandService._runbook_applies_to_incident(runbook, incident): return { 'error': f'Runbook "{runbook.name}" does not apply to this incident type', 'incident_category': incident.category, 'incident_severity': incident.severity } # Execute the runbook execution = RunbookExecution.objects.create( runbook=runbook, incident=incident, triggered_by=user, trigger_type='CHAT_COMMAND', trigger_data={ 'chat_command_id': str(chat_command.id), 'command_text': chat_command.command_text } ) # Update chat command with execution reference chat_command.automation_execution = execution chat_command.save() # Create status message in chat AutomationCommandService._create_execution_status_message( chat_command.message.war_room, f"🚀 **Runbook Execution Started**\n\n" f"**Runbook:** {runbook.name}\n" f"**Execution ID:** {execution.id}\n" f"**Triggered by:** {user.username}\n" f"**Status:** {execution.status}\n\n" f"Monitor progress in the automation dashboard." ) return { 'success': True, 'execution_id': str(execution.id), 'runbook_name': runbook.name, 'status': execution.status, 'message': f'Runbook "{runbook.name}" execution started successfully' } except Exception as e: return { 'error': f'Failed to execute runbook: {str(e)}' } @staticmethod def execute_auto_remediation_command(chat_command: ChatCommand, remediation_name: str, user: User) -> Dict[str, Any]: """Execute auto-remediation via chat command""" try: incident = chat_command.message.war_room.incident # Find the auto-remediation remediation = AutoRemediation.objects.filter( name__icontains=remediation_name, is_active=True ).first() if not remediation: return { 'error': f'Auto-remediation "{remediation_name}" not found or inactive', 'suggestions': AutomationCommandService._get_remediation_suggestions(remediation_name) } # Check if remediation applies to this incident if not AutomationCommandService._remediation_applies_to_incident(remediation, incident): return { 'error': f'Auto-remediation "{remediation.name}" does not apply to this incident type', 'incident_category': incident.category, 'incident_severity': incident.severity } # Execute the auto-remediation execution = AutoRemediationExecution.objects.create( auto_remediation=remediation, incident=incident, triggered_by=user, trigger_type='CHAT_COMMAND', trigger_data={ 'chat_command_id': str(chat_command.id), 'command_text': chat_command.command_text } ) # Create status message in chat AutomationCommandService._create_execution_status_message( chat_command.message.war_room, f"🔧 **Auto-Remediation Started**\n\n" f"**Remediation:** {remediation.name}\n" f"**Execution ID:** {execution.id}\n" f"**Triggered by:** {user.username}\n" f"**Status:** {execution.status}\n\n" f"Monitor progress in the automation dashboard." ) return { 'success': True, 'execution_id': str(execution.id), 'remediation_name': remediation.name, 'status': execution.status, 'message': f'Auto-remediation "{remediation.name}" execution started successfully' } except Exception as e: return { 'error': f'Failed to execute auto-remediation: {str(e)}' } @staticmethod def get_incident_status(chat_command: ChatCommand) -> Dict[str, Any]: """Get comprehensive incident status""" try: incident = chat_command.message.war_room.incident # Get SLA status from .sla_notifications import SLANotificationService sla_status = SLANotificationService.get_sla_status_for_incident(incident) # Get recent runbook executions recent_executions = RunbookExecution.objects.filter( incident=incident ).order_by('-created_at')[:5] executions_data = [] for execution in recent_executions: executions_data.append({ 'id': str(execution.id), 'runbook_name': execution.runbook.name, 'status': execution.status, 'started_at': execution.started_at.isoformat() if execution.started_at else None, 'completed_at': execution.completed_at.isoformat() if execution.completed_at else None }) return { 'incident_id': str(incident.id), 'title': incident.title, 'status': incident.status, 'severity': incident.severity, 'priority': incident.priority, 'category': incident.category, 'assigned_to': incident.assigned_to.username if incident.assigned_to else None, 'reporter': incident.reporter.username if incident.reporter else None, 'created_at': incident.created_at.isoformat(), 'updated_at': incident.updated_at.isoformat(), 'resolution_time': str(incident.resolution_time) if incident.resolution_time else None, 'sla_status': sla_status, 'recent_executions': executions_data, 'automation_enabled': incident.automation_enabled, 'runbook_suggested': incident.runbook_suggested, 'auto_remediation_attempted': incident.auto_remediation_attempted } except Exception as e: return { 'error': f'Failed to get incident status: {str(e)}' } @staticmethod def list_available_runbooks(incident: Incident) -> List[Dict[str, Any]]: """List runbooks available for the incident""" try: runbooks = Runbook.objects.filter(is_active=True) available_runbooks = [] for runbook in runbooks: if AutomationCommandService._runbook_applies_to_incident(runbook, incident): available_runbooks.append({ 'id': str(runbook.id), 'name': runbook.name, 'description': runbook.description, 'category': runbook.category, 'severity_levels': runbook.severity_levels, 'estimated_duration': runbook.estimated_duration }) return available_runbooks except Exception as e: return [] @staticmethod def list_available_remediations(incident: Incident) -> List[Dict[str, Any]]: """List auto-remediations available for the incident""" try: remediations = AutoRemediation.objects.filter(is_active=True) available_remediations = [] for remediation in remediations: if AutomationCommandService._remediation_applies_to_incident(remediation, incident): available_remediations.append({ 'id': str(remediation.id), 'name': remediation.name, 'description': remediation.description, 'category': remediation.category, 'severity_levels': remediation.severity_levels, 'estimated_duration': remediation.estimated_duration }) return available_remediations except Exception as e: return [] @staticmethod def _runbook_applies_to_incident(runbook: Runbook, incident: Incident) -> bool: """Check if runbook applies to the incident""" # Check categories if runbook.categories and incident.category not in runbook.categories: return False # Check severity levels if runbook.severity_levels and incident.severity not in runbook.severity_levels: return False # Check if runbook is active if not runbook.is_active: return False return True @staticmethod def _remediation_applies_to_incident(remediation: AutoRemediation, incident: Incident) -> bool: """Check if auto-remediation applies to the incident""" # Check categories if remediation.categories and incident.category not in remediation.categories: return False # Check severity levels if remediation.severity_levels and incident.severity not in remediation.severity_levels: return False # Check if remediation is active if not remediation.is_active: return False return True @staticmethod def _get_runbook_suggestions(partial_name: str) -> List[str]: """Get runbook name suggestions based on partial input""" try: runbooks = Runbook.objects.filter( name__icontains=partial_name, is_active=True ).values_list('name', flat=True)[:5] return list(runbooks) except: return [] @staticmethod def _get_remediation_suggestions(partial_name: str) -> List[str]: """Get auto-remediation name suggestions based on partial input""" try: remediations = AutoRemediation.objects.filter( name__icontains=partial_name, is_active=True ).values_list('name', flat=True)[:5] return list(remediations) except: return [] @staticmethod def _create_execution_status_message(war_room: 'WarRoom', content: str): """Create a status message in the war room""" try: WarRoomMessage.objects.create( war_room=war_room, content=content, message_type='SYSTEM', sender=None, sender_name='Automation System', external_data={ 'message_type': 'automation_status' } ) except Exception as e: print(f"Error creating status message: {e}") @staticmethod def update_execution_status(execution_id: str, status: str, result: Dict[str, Any] = None): """Update execution status and notify chat room""" try: # Find the chat command that triggered this execution chat_command = ChatCommand.objects.filter( automation_execution_id=execution_id ).first() if not chat_command: return False # Update chat command status chat_command.execution_status = status if result: chat_command.execution_result = result chat_command.save() # Create status update message status_emoji = { 'SUCCESS': '✅', 'FAILED': '❌', 'RUNNING': '🔄', 'CANCELLED': 'âšī¸' }.get(status, '📊') message_content = ( f"{status_emoji} **Execution Status Update**\n\n" f"**Status:** {status}\n" f"**Execution ID:** {execution_id}\n" ) if result: if 'error' in result: message_content += f"**Error:** {result['error']}\n" if 'output' in result: message_content += f"**Output:** {result['output'][:200]}...\n" AutomationCommandService._create_execution_status_message( chat_command.message.war_room, message_content ) return True except Exception as e: print(f"Error updating execution status: {e}") return False