fix: ansible routes validation, SSE dedup, scoped querySelector, status_color fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# fablednetmon/ansible/routes.py
|
# fablednetmon/ansible/routes.py
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import asyncio
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -77,6 +78,9 @@ async def create_run():
|
|||||||
source_name = form.get("source_name", "").strip()
|
source_name = form.get("source_name", "").strip()
|
||||||
inventory_path = form.get("inventory_path", "").strip()
|
inventory_path = form.get("inventory_path", "").strip()
|
||||||
|
|
||||||
|
if not playbook_path or not inventory_path:
|
||||||
|
return "playbook_path and inventory_path are required", 400
|
||||||
|
|
||||||
all_sources = _get_sources()
|
all_sources = _get_sources()
|
||||||
source = next((s for s in all_sources if s["name"] == source_name), None)
|
source = next((s for s in all_sources if s["name"] == source_name), None)
|
||||||
if source is None:
|
if source is None:
|
||||||
@@ -98,8 +102,7 @@ async def create_run():
|
|||||||
async with db.begin():
|
async with db.begin():
|
||||||
db.add(run)
|
db.add(run)
|
||||||
|
|
||||||
import asyncio
|
task = asyncio.create_task(
|
||||||
asyncio.create_task(
|
|
||||||
executor.start_run(
|
executor.start_run(
|
||||||
current_app._get_current_object(), # type: ignore[attr-defined]
|
current_app._get_current_object(), # type: ignore[attr-defined]
|
||||||
run_id,
|
run_id,
|
||||||
@@ -108,6 +111,11 @@ async def create_run():
|
|||||||
source["path"],
|
source["path"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
task.add_done_callback(
|
||||||
|
lambda t: t.exception() and current_app.logger.error(
|
||||||
|
"Ansible run %s raised: %s", run_id, t.exception()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return await render_template(
|
return await render_template(
|
||||||
"ansible/run_started.html",
|
"ansible/run_started.html",
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
<div style="display:flex;gap:0.75rem;align-items:center;">
|
<div style="display:flex;gap:0.75rem;align-items:center;">
|
||||||
<button type="button" class="btn"
|
<button type="button" class="btn"
|
||||||
onclick="var manual=document.getElementById('manual-inv-{{ sd.source.name }}').value.trim();
|
onclick="var manual=document.getElementById('manual-inv-{{ sd.source.name }}').value.trim();
|
||||||
if(manual){document.querySelector('[name=inventory_path]').value=manual;}
|
if(manual){this.closest('form').querySelector('[name=inventory_path]').value=manual;}
|
||||||
htmx.trigger(this.closest('form'),'submit');">Execute</button>
|
htmx.trigger(this.closest('form'),'submit');">Execute</button>
|
||||||
<a href="#" onclick="document.getElementById('run-form-{{ sd.source.name }}').style.display='none';return false;"
|
<a href="#" onclick="document.getElementById('run-form-{{ sd.source.name }}').style.display='none';return false;"
|
||||||
style="color:#606080;font-size:0.9rem;">Cancel</a>
|
style="color:#606080;font-size:0.9rem;">Cancel</a>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for run in runs %}
|
{% for run in runs %}
|
||||||
{% set status_color = {"running": "#a0a000", "success": "#40a040", "failed": "#a04040", "interrupted": "#806040"}[run.status.value] %}
|
{% set status_color = {"running": "#a0a000", "success": "#40a040", "failed": "#a04040", "interrupted": "#806040"}.get(run.status.value, "#808080") %}
|
||||||
<tr style="border-top:1px solid #2a2a4a;">
|
<tr style="border-top:1px solid #2a2a4a;">
|
||||||
<td style="padding:0.75rem 1rem;color:#e0e0e0;font-size:0.9rem;">{{ run.playbook_path }}</td>
|
<td style="padding:0.75rem 1rem;color:#e0e0e0;font-size:0.9rem;">{{ run.playbook_path }}</td>
|
||||||
<td style="padding:0.75rem 1rem;color:#8080a0;font-size:0.9rem;">{{ run.source_name }}</td>
|
<td style="padding:0.75rem 1rem;color:#8080a0;font-size:0.9rem;">{{ run.source_name }}</td>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<h1 style="color:#c0c0ff;">Run Detail</h1>
|
<h1 style="color:#c0c0ff;">Run Detail</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% set status_color = {"running": "#a0a000", "success": "#40a040", "failed": "#a04040", "interrupted": "#806040"}[run.status.value] %}
|
{% set status_color = {"running": "#a0a000", "success": "#40a040", "failed": "#a04040", "interrupted": "#806040"}.get(run.status.value, "#808080") %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div style="display:grid;grid-template-columns:auto 1fr;gap:0.4rem 1rem;font-size:0.9rem;margin-bottom:1rem;">
|
<div style="display:grid;grid-template-columns:auto 1fr;gap:0.4rem 1rem;font-size:0.9rem;margin-bottom:1rem;">
|
||||||
<span style="color:#606080;">ID</span><span style="color:#a0a0c0;font-family:monospace;">{{ run.id }}</span>
|
<span style="color:#606080;">ID</span><span style="color:#a0a0c0;font-family:monospace;">{{ run.id }}</span>
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
<span style="color:#a0a0c0;font-size:0.9rem;">{{ run.playbook_path }}</span>
|
<span style="color:#a0a0c0;font-size:0.9rem;">{{ run.playbook_path }}</span>
|
||||||
<a href="/ansible/runs/{{ run.id }}" style="color:#6060c0;font-size:0.82rem;margin-left:auto;">Full view →</a>
|
<a href="/ansible/runs/{{ run.id }}" style="color:#6060c0;font-size:0.82rem;margin-left:auto;">Full view →</a>
|
||||||
</div>
|
</div>
|
||||||
<div hx-ext="sse" sse-connect="/ansible/runs/{{ run.id }}/stream">
|
|
||||||
<pre id="sse-output-{{ run.id }}"
|
<pre id="sse-output-{{ run.id }}"
|
||||||
style="background:#080810;padding:0.75rem;border-radius:4px;font-family:monospace;font-size:0.78rem;color:#a0c0a0;max-height:400px;overflow-y:auto;white-space:pre-wrap;margin:0;"
|
style="background:#080810;padding:0.75rem;border-radius:4px;font-family:monospace;font-size:0.78rem;color:#a0c0a0;max-height:400px;overflow-y:auto;white-space:pre-wrap;margin:0 0 0.5rem 0;">
|
||||||
sse-swap="output"
|
|
||||||
hx-swap="beforeend">
|
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
<div id="sse-status-{{ run.id }}" style="font-size:0.85rem;color:#606080;">
|
||||||
<div id="sse-status-{{ run.id }}" style="margin-top:0.5rem;font-size:0.85rem;color:#606080;">
|
|
||||||
Streaming output…
|
Streaming output…
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
Reference in New Issue
Block a user