Files
Server_Monitorizare_v2/templates/ansible/executions.html
2026-04-23 15:55:46 +03:00

311 lines
14 KiB
HTML

{% extends "base.html" %}
{% block title %}Execution History - Server Monitoring{% endblock %}
{% block page_title %}Ansible Execution History{% endblock %}
{% block extra_css %}
<style>
.execution-card {
border: none;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.execution-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}
.status-running {
background: linear-gradient(45deg, #007bff, #0056b3);
}
.status-completed {
background: linear-gradient(45deg, #28a745, #1e7e34);
}
.status-failed {
background: linear-gradient(45deg, #dc3545, #bd2130);
}
.status-queued {
background: linear-gradient(45deg, #6c757d, #545b62);
}
.execution-details {
background-color: #f8f9fa;
border-radius: 8px;
padding: 1rem;
}
.host-result {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 15px;
font-size: 0.8rem;
margin: 0.1rem;
}
.host-success { background-color: #d4edda; color: #155724; }
.host-failed { background-color: #f8d7da; color: #721c24; }
.host-unreachable { background-color: #f8d7da; color: #721c24; }
.host-skipped { background-color: #fff3cd; color: #856404; }
</style>
{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-md-8">
<h4><i class="fas fa-history"></i> Ansible Execution History</h4>
<small class="text-muted">Track and monitor all playbook executions</small>
</div>
<div class="col-md-4 text-end">
<a href="{{ url_for('ansible_web.execute') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> New Execution
</a>
<button class="btn btn-outline-secondary ms-2" onclick="refreshPage()">
<i class="fas fa-sync"></i> Refresh
</button>
</div>
</div>
<!-- Execution Statistics -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h3 class="text-primary">{{ executions|length }}</h3>
<p class="mb-0">Total Executions</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h3 class="text-success">{{ executions|selectattr('status', 'equalto', 'completed')|list|length }}</h3>
<p class="mb-0">Completed</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h3 class="text-info">{{ executions|selectattr('status', 'equalto', 'running')|list|length }}</h3>
<p class="mb-0">Running</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h3 class="text-danger">{{ executions|selectattr('status', 'equalto', 'failed')|list|length }}</h3>
<p class="mb-0">Failed</p>
</div>
</div>
</div>
</div>
<!-- Execution List -->
<div class="row">
<div class="col-12">
{% if executions %}
{% for execution in executions %}
<div class="execution-card card">
<div class="card-header status-{{ execution.status }} text-white d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-0">
<i class="fas fa-play"></i> {{ execution.playbook_name }}
{% if execution.status == 'running' %}
<span class="spinner-border spinner-border-sm ms-2"></span>
{% endif %}
</h6>
<small>Execution ID: {{ execution.execution_id }}</small>
</div>
<div class="text-end">
<span class="badge bg-light text-dark">{{ execution.status|title }}</span>
{% if execution.priority and execution.priority != 5 %}
<span class="badge bg-warning text-dark">Priority: {{ execution.priority }}</span>
{% endif %}
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="execution-details">
<h6><i class="fas fa-info-circle"></i> Execution Details</h6>
<div class="row">
<div class="col-6">
<small class="text-muted">Started:</small><br>
{{ execution.started_at.strftime('%Y-%m-%d %H:%M') if execution.started_at else 'Queued' }}
</div>
<div class="col-6">
<small class="text-muted">Duration:</small><br>
{% if execution.duration %}
{{ execution.duration_formatted }}
{% elif execution.status == 'running' and execution.started_at %}
<span id="duration-{{ execution.id }}">Calculating...</span>
{% else %}
-
{% endif %}
</div>
</div>
<div class="row mt-2">
<div class="col-6">
<small class="text-muted">User:</small><br>
{{ execution.execution_user or 'System' }}
</div>
<div class="col-6">
<small class="text-muted">Target Hosts:</small><br>
{{ execution.total_hosts or 0 }}
</div>
</div>
</div>
</div>
<div class="col-md-6">
{% if execution.status in ['completed', 'failed'] %}
<div class="execution-details">
<h6><i class="fas fa-chart-bar"></i> Results Summary</h6>
<div class="mb-2">
{% if execution.successful_hosts > 0 %}
<span class="host-result host-success">✓ {{ execution.successful_hosts }} successful</span>
{% endif %}
{% if execution.failed_hosts > 0 %}
<span class="host-result host-failed">✗ {{ execution.failed_hosts }} failed</span>
{% endif %}
{% if execution.unreachable_hosts > 0 %}
<span class="host-result host-unreachable">⚠ {{ execution.unreachable_hosts }} unreachable</span>
{% endif %}
{% if execution.skipped_hosts > 0 %}
<span class="host-result host-skipped">⊝ {{ execution.skipped_hosts }} skipped</span>
{% endif %}
</div>
{% if execution.summary_message %}
<small class="text-muted">{{ execution.summary_message }}</small>
{% endif %}
</div>
{% elif execution.status == 'running' %}
<div class="execution-details">
<h6><i class="fas fa-spinner fa-spin"></i> In Progress</h6>
<div class="progress mb-2">
<div class="progress-bar progress-bar-striped progress-bar-animated"
style="width: 100%"></div>
</div>
<small class="text-muted">Execution is currently running...</small>
</div>
{% else %}
<div class="execution-details">
<h6><i class="fas fa-clock"></i> Queued</h6>
{% if execution.queue_position > 0 %}
<p class="mb-0">Position in queue: {{ execution.queue_position }}</p>
{% endif %}
<small class="text-muted">Waiting for execution...</small>
</div>
{% endif %}
</div>
</div>
<!-- Action Buttons -->
<div class="mt-3 text-end">
<a href="{{ url_for('ansible_web.execution_details', execution_id=execution.execution_id) }}"
class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye"></i> View Details
</a>
{% if execution.status == 'running' %}
<button class="btn btn-outline-warning btn-sm ms-1"
onclick="cancelExecution('{{ execution.execution_id }}')">
<i class="fas fa-stop"></i> Cancel
</button>
{% elif execution.status == 'failed' and execution.retry_count < execution.max_retries %}
<button class="btn btn-outline-success btn-sm ms-1"
onclick="retryExecution('{{ execution.execution_id }}')">
<i class="fas fa-redo"></i> Retry
</button>
{% endif %}
{% if execution.ansible_log_file %}
<a href="{{ url_for('ansible_web.download_log', execution_id=execution.execution_id) }}"
class="btn btn-outline-secondary btn-sm ms-1">
<i class="fas fa-download"></i> Download Log
</a>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="card">
<div class="card-body text-center">
<i class="fas fa-info-circle fa-3x text-muted mb-3"></i>
<h5>No Executions Found</h5>
<p class="text-muted">No playbook executions have been run yet.</p>
<a href="{{ url_for('ansible_web.execute') }}" class="btn btn-primary">
<i class="fas fa-play"></i> Run Your First Playbook
</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<script>
function refreshPage() {
location.reload();
}
function cancelExecution(executionId) {
if (confirm('Are you sure you want to cancel this execution?')) {
fetch(`/api/ansible/executions/${executionId}/cancel`, { method: 'POST' })
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Failed to cancel execution: ' + data.error);
}
});
}
}
function retryExecution(executionId) {
if (confirm('Are you sure you want to retry this execution?')) {
fetch(`/api/ansible/executions/${executionId}/retry`, { method: 'POST' })
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Execution queued for retry');
location.reload();
} else {
alert('Failed to retry execution: ' + data.error);
}
});
}
}
// Auto-refresh running executions every 30 seconds
setInterval(() => {
const runningExecutions = document.querySelectorAll('.status-running');
if (runningExecutions.length > 0) {
location.reload();
}
}, 30000);
// Update running durations
function updateRunningDurations() {
document.querySelectorAll('[id^="duration-"]').forEach(element => {
const executionId = element.id.replace('duration-', '');
// This would need to be implemented to calculate current duration
// For now, show a simple indicator
element.textContent = 'Running...';
});
}
setInterval(updateRunningDurations, 1000);
</script>
{% endblock %}