Initial commit — Server_Monitorizare_v2
This commit is contained in:
311
templates/ansible/executions.html
Normal file
311
templates/ansible/executions.html
Normal file
@@ -0,0 +1,311 @@
|
||||
{% 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 %}
|
||||
Reference in New Issue
Block a user