#5212 "Plain labels: no raw ids, scopes, metric keys or unexplained jargon", batch 2: - A run records where it came from: AnsibleRun.origin ("schedule", "alert" or "mcp") and origin_id, set by the scheduler, Run now, alert actions and the MCP tools. Migration 0031 backfills past runs from alert events, schedules' last runs, and MCP audit entries. - The runs list has a Started by column: "schedule nightly", "alert rule nas down", "alice via MCP" or "alice", linked where there is a page. The raw scope tooltip is gone. - Run detail: no run id in the subtitle or body, Started by in words, and the host summary lists failed and unreachable hosts first. The live output only follows new lines while the reader is at the bottom, and when the run finishes the page reloads once so the badge and summary are final. - list_ansible_runs and get_ansible_run report origin and origin_id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
53 lines
2.6 KiB
Python
53 lines
2.6 KiB
Python
"""Who or what started each run, as the runs list and run detail say it.
|
|
|
|
A run records the user who started it (triggered_by) and where it came from
|
|
(origin / origin_id, migration 0031): a schedule, an alert rule, or an agent
|
|
over MCP. This turns those into "schedule nightly", "alert rule nas down",
|
|
"alice via MCP" or "alice", in three queries for a whole page of runs.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import select
|
|
|
|
from steward.models.alerts import AlertRule
|
|
from steward.models.ansible_schedule import AnsibleSchedule
|
|
from steward.models.users import User
|
|
|
|
|
|
def describe_origin(run, users: dict, schedules: dict, rules: dict) -> dict:
|
|
"""{"text", "url", "kind"} for one run, from id → name maps. A schedule or
|
|
rule deleted since is named as such; a run with neither a user nor an
|
|
origin predates origins and was started by Steward itself."""
|
|
who = users.get(run.triggered_by, "a deleted user") if run.triggered_by else None
|
|
if run.origin == "schedule":
|
|
name = schedules.get(run.origin_id)
|
|
text = f"schedule {name}" if name else "a deleted schedule"
|
|
if who:
|
|
text += f" (run now by {who})"
|
|
return {"text": text, "url": "/ansible/schedules" if name else None, "kind": "schedule"}
|
|
if run.origin == "alert":
|
|
name = rules.get(run.origin_id)
|
|
return {"text": f"alert rule {name}" if name else "a deleted alert rule",
|
|
"url": f"/alerts/history?rule={run.origin_id}" if name else None, "kind": "alert"}
|
|
if run.origin == "mcp":
|
|
return {"text": f"{who or 'an agent'} via MCP", "url": None, "kind": "mcp"}
|
|
if who:
|
|
return {"text": who, "url": None, "kind": "user"}
|
|
return {"text": "Steward", "url": None, "kind": "system"}
|
|
|
|
|
|
async def run_origins(db, runs) -> dict[str, dict]:
|
|
"""{run id: describe_origin(run)} for a page of runs."""
|
|
runs = list(runs)
|
|
user_ids = {r.triggered_by for r in runs if r.triggered_by}
|
|
sched_ids = {r.origin_id for r in runs if r.origin == "schedule" and r.origin_id}
|
|
rule_ids = {r.origin_id for r in runs if r.origin == "alert" and r.origin_id}
|
|
users = dict((await db.execute(
|
|
select(User.id, User.username).where(User.id.in_(user_ids)))).all()) if user_ids else {}
|
|
schedules = dict((await db.execute(
|
|
select(AnsibleSchedule.id, AnsibleSchedule.name).where(AnsibleSchedule.id.in_(sched_ids)))).all()) \
|
|
if sched_ids else {}
|
|
rules = dict((await db.execute(
|
|
select(AlertRule.id, AlertRule.name).where(AlertRule.id.in_(rule_ids)))).all()) if rule_ids else {}
|
|
return {r.id: describe_origin(r, users, schedules, rules) for r in runs}
|