feat: execution failure reports, auto-printer for WMT, UTC timezone fix for all timestamps

This commit is contained in:
ske087
2026-04-24 15:52:12 +03:00
parent d2485e4c66
commit 056f467791
27 changed files with 1391 additions and 285 deletions

View File

@@ -3,7 +3,7 @@ Web routes for Ansible management interface
"""
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from app.services.ansible_service import AnsibleService
from app.models import Device, AnsibleExecution, PlaybookExecution
from app.models import Device, AnsibleExecution, PlaybookExecution, ExecutionFailureReport
from config.database_config import get_db
import logging
@@ -89,13 +89,18 @@ def playbooks():
'builtin': True
},
{
'name': 'restart_service',
'name': 'restart_service',
'description': 'Restart monitoring services on devices',
'builtin': True
}
},
{
'name': 'distribute_ssh_keys',
'description': 'Push server public key to devices using password auth',
'builtin': True
},
]
return render_template('ansible/playbooks.html',
return render_template('ansible/playbooks.html',
playbooks=playbooks,
builtin_playbooks=builtin_playbooks)
except Exception as e:
@@ -123,17 +128,20 @@ def execute():
})
seen.add(h['hostname'])
settings = ansible_service.load_settings()
return render_template('ansible/execute.html',
inventory=inventory_data,
all_inv_hosts=all_inv_hosts,
preselect_playbook=preselect)
preselect_playbook=preselect,
use_password_auth=settings.get('use_password_auth', False))
except Exception as e:
logging.error(f"Error loading execute form: {e}")
flash(f'Error loading form: {e}', 'error')
return render_template('ansible/execute.html',
inventory={'groups': {}},
all_inv_hosts=[],
preselect_playbook='')
preselect_playbook='',
use_password_auth=False)
elif request.method == 'POST':
# Execute playbook
@@ -182,17 +190,24 @@ def execute():
ansible_service.create_restart_service_playbook()
elif playbook_name == 'system_health':
ansible_service.create_system_health_playbook()
elif playbook_name == 'distribute_ssh_keys':
ansible_service.create_distribute_ssh_keys_playbook()
# Add controller IP for callbacks
extra_vars['ansible_controller_ip'] = request.host
# Force password auth for key distribution, or honour the form toggle
force_password = (playbook_name == 'distribute_ssh_keys') or \
bool(request.form.get('force_password_auth'))
# Use async execution (returns immediately with execution_id)
result = ansible_service.execute_playbook_async(
playbook_name=playbook_name,
limit_hosts=selected_hosts,
extra_vars=extra_vars,
priority=priority,
max_retries=max_retries
max_retries=max_retries,
force_password_auth=force_password,
)
if result['success']:
@@ -256,6 +271,11 @@ def execution_details(execution_id):
flash(f'Error loading execution details: {e}', 'error')
return redirect(url_for('ansible_web.executions'))
@ansible_web_bp.route('/executions/<execution_id>/live-popup')
def execution_live_popup(execution_id):
"""Standalone popup window for live execution output"""
return render_template('ansible/live_popup.html', execution_id=execution_id)
@ansible_web_bp.route('/ssh/setup')
def ssh_setup():
"""SSH key setup interface"""
@@ -288,10 +308,14 @@ def save_ssh_settings():
try:
fallback_password = request.form.get('ssh_fallback_password', '').strip()
if not fallback_password:
flash('Fallback password cannot be empty.', 'error')
flash('Password cannot be empty.', 'error')
return redirect(url_for('ansible_web.ssh_setup'))
ansible_service.save_settings({'ssh_fallback_password': fallback_password})
use_password_auth = request.form.get('use_password_auth') == 'on'
ansible_service.save_settings({
'ssh_fallback_password': fallback_password,
'use_password_auth': use_password_auth,
})
flash('SSH settings saved successfully.', 'success')
except Exception as e:
logging.error(f"Error saving SSH settings: {e}")
@@ -596,4 +620,20 @@ def delete_playbook():
except Exception as e:
logging.error(f"Error deleting playbook: {e}")
return jsonify({'error': str(e)}), 500
return jsonify({'error': str(e)}), 500
@ansible_web_bp.route('/failure-reports')
def failure_reports():
"""View all saved execution failure reports."""
try:
with get_db().get_session() as session:
reports = session.query(ExecutionFailureReport)\
.order_by(ExecutionFailureReport.saved_at.desc())\
.all()
reports_data = [r.to_dict() for r in reports]
return render_template('ansible/failure_reports.html', reports=reports_data)
except Exception as e:
logging.error(f"Error loading failure reports: {e}")
flash(f'Error loading failure reports: {e}', 'error')
return render_template('ansible/failure_reports.html', reports=[])