# apps/devops/admin.py """ Admin panel configurations for the DevOps application. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from django.utils import timezone from django.db.models import Count, Q import json from apps.common.admin_mixins import SoftDeleteAdminMixin from .models import ( EnvironmentConfig, SecretConfig, DeploymentHistory, HealthCheck, ServiceMonitoring ) @admin.register(EnvironmentConfig) class EnvironmentConfigAdmin(SoftDeleteAdminMixin, admin.ModelAdmin): """Admin configuration for EnvironmentConfig.""" list_display = [ 'name', 'environment_type', 'is_active', 'secrets_count', 'deployments_count', 'created_at' ] list_filter = ['environment_type', 'is_active', 'created_at'] search_fields = ['name', 'description'] readonly_fields = ['created_at', 'updated_at'] fieldsets = ( ('Basic Information', { 'fields': ('name', 'environment_type', 'description', 'is_active') }), ('Audit Information', { 'fields': ('created_by', 'created_at', 'updated_at'), 'classes': ('collapse',) }), ) def secrets_count(self, obj): count = obj.secrets.filter(is_active=True).count() return format_html( '{}', 'green' if count > 0 else 'red', count ) secrets_count.short_description = 'Secrets Count' def deployments_count(self, obj): count = obj.deployments.count() return format_html( '{}', reverse('admin:devops_deploymenthistory_changelist'), obj.id, count ) deployments_count.short_description = 'Deployments Count' def get_queryset(self, request): queryset = super().get_queryset(request) return queryset.annotate( secrets_count=Count('secrets', filter=Q(secrets__is_active=True)), deployments_count=Count('deployments') ) @admin.register(SecretConfig) class SecretConfigAdmin(SoftDeleteAdminMixin, admin.ModelAdmin): """Admin configuration for SecretConfig.""" list_display = [ 'key_name', 'environment', 'category', 'is_active', 'is_expired_display', 'created_at' ] list_filter = ['environment', 'category', 'is_active', 'created_at'] search_fields = ['key_name', 'description'] readonly_fields = ['created_at', 'updated_at', 'is_expired_display'] fieldsets = ( ('Basic Information', { 'fields': ('environment', 'key_name', 'encrypted_value', 'category') }), ('Advanced Configuration', { 'fields': ('description', 'is_active', 'expires_at'), 'classes': ('collapse',) }), ('Audit Information', { 'fields': ('created_by', 'created_at', 'updated_at', 'is_expired_display'), 'classes': ('collapse',) }), ) def is_expired_display(self, obj): if obj.expires_at is None: return format_html('{}', "No Expiration") if obj.is_expired: return format_html('{}', "Expired") else: days_left = (obj.expires_at - timezone.now()).days color = 'orange' if days_left <= 7 else 'green' return format_html( '{} days left', color, days_left ) is_expired_display.short_description = 'Expiration Status' def get_queryset(self, request): return super().get_queryset(request).select_related('environment') @admin.register(DeploymentHistory) class DeploymentHistoryAdmin(admin.ModelAdmin): """Admin configuration for DeploymentHistory.""" list_display = [ 'environment', 'version', 'status_display', 'branch', 'deployed_by', 'duration_display', 'started_at' ] list_filter = ['environment', 'status', 'branch', 'started_at'] search_fields = ['version', 'commit_hash', 'deployed_by__username'] readonly_fields = [ 'started_at', 'completed_at', 'duration_display', 'deployment_logs_display' ] fieldsets = ( ('Deployment Info', { 'fields': ('environment', 'version', 'commit_hash', 'branch', 'status') }), ('Timestamps', { 'fields': ('started_at', 'completed_at', 'duration_display') }), ('Details', { 'fields': ( 'deployed_by', 'artifacts_url', 'rollback_from', 'deployment_logs_display' ), 'classes': ('collapse',) }), ) def status_display(self, obj): colors = { 'pending': 'orange', 'running': 'blue', 'success': 'green', 'failed': 'red', 'cancelled': 'gray', 'rollback': 'purple', } return format_html( '{}', colors.get(obj.status, 'black'), obj.get_status_display() ) status_display.short_description = 'Status' def duration_display(self, obj): if obj.duration: return f"{obj.duration.total_seconds():.0f} seconds" return "Unknown" duration_display.short_description = 'Duration' def deployment_logs_display(self, obj): if obj.deployment_logs: return format_html( '
{}',
obj.deployment_logs[:1000] + ('...' if len(obj.deployment_logs) > 1000 else '')
)
return "No logs available"
deployment_logs_display.short_description = 'Deployment Logs'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'environment', 'deployed_by', 'rollback_from'
)
@admin.register(HealthCheck)
class HealthCheckAdmin(admin.ModelAdmin):
"""Admin configuration for HealthCheck."""
list_display = [
'service_name', 'environment', 'status_display',
'response_time_display', 'status_code', 'checked_at'
]
list_filter = ['environment', 'status', 'service_name', 'checked_at']
search_fields = ['service_name', 'endpoint_url', 'error_message']
readonly_fields = ['checked_at', 'response_data_display']
fieldsets = (
('Basic Information', {
'fields': ('environment', 'service_name', 'endpoint_url', 'status')
}),
('Check Results', {
'fields': (
'response_time', 'status_code', 'error_message',
'response_data_display', 'checked_at'
)
}),
)
def status_display(self, obj):
colors = {
'healthy': 'green',
'warning': 'orange',
'critical': 'red',
'unknown': 'gray',
}
return format_html(
'{}',
colors.get(obj.status, 'black'),
obj.get_status_display()
)
status_display.short_description = 'Status'
def response_time_display(self, obj):
if obj.response_time is not None:
color = 'green' if obj.response_time < 1000 else 'orange' if obj.response_time < 5000 else 'red'
return format_html(
'{:.0f} ms',
color,
obj.response_time
)
return "Unknown"
response_time_display.short_description = 'Response Time'
def response_data_display(self, obj):
if obj.response_data:
return format_html(
'{}',
json.dumps(obj.response_data, indent=2, ensure_ascii=False)
)
return "No data available"
response_data_display.short_description = 'Response Data'
def get_queryset(self, request):
return super().get_queryset(request).select_related('environment')
@admin.register(ServiceMonitoring)
class ServiceMonitoringAdmin(admin.ModelAdmin):
"""Admin configuration for ServiceMonitoring."""
list_display = [
'service_name', 'environment', 'service_type',
'is_active', 'alert_on_failure', 'check_interval',
'last_check_status', 'updated_at'
]
list_filter = ['environment', 'service_type', 'is_active', 'alert_on_failure']
search_fields = ['service_name', 'health_check_url']
readonly_fields = ['created_at', 'updated_at', 'last_check_status']
fieldsets = (
('Service Information', {
'fields': ('environment', 'service_name', 'service_type', 'health_check_url')
}),
('Monitoring Configuration', {
'fields': ('check_interval', 'timeout', 'is_active', 'alert_on_failure')
}),
('Audit Information', {
'fields': ('created_at', 'updated_at', 'last_check_status'),
'classes': ('collapse',)
}),
)
def last_check_status(self, obj):
latest_check = obj.environment.health_checks.filter(
service_name=obj.service_name
).first()
if latest_check:
colors = {
'healthy': 'green',
'warning': 'orange',
'critical': 'red',
'unknown': 'gray',
}
return format_html(
'{} ({})',
colors.get(latest_check.status, 'black'),
latest_check.get_status_display(),
latest_check.checked_at.strftime('%H:%M')
)
return format_html('{}', "Not Checked Yet")
last_check_status.short_description = 'Last Check Status'
def get_queryset(self, request):
return super().get_queryset(request).select_related('environment')