feat(hosts): Phase 2+3 — agent column, fold fleet + management into Hosts
- Hosts list: new Agent column (latest CPU/mem read from the generic
PluginMetric table — no host_agent import) + an admin "Agent fleet" button.
- /plugins/host_agent/ (old fleet page) now redirects to /hosts/ (folded into
the hub; kept as a redirect so widgets/links don't 404).
- Agent management moved off the "settings" URL: the management page is now
/plugins/host_agent/fleet/ ("Agent fleet" — bulk provision/install/update +
registrations + curl install), reachable from the Hosts list. Old
/plugins/host_agent/settings/ redirects there. Per-host management lives on
the host detail page; this page is now explicitly the bulk/fleet view.
Milestone 70 phases 2-3. Phase 4 (plugins capability/integration split + nav
cleanup) next.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -375,10 +375,9 @@ async def _fleet_rows(session) -> list[dict]:
|
||||
@host_agent_bp.get("/")
|
||||
@require_role(UserRole.viewer)
|
||||
async def index():
|
||||
"""Fleet overview page — cards per host linking to the detail view."""
|
||||
async with current_app.db_sessionmaker() as session:
|
||||
rows = await _fleet_rows(session)
|
||||
return await render_template("host_list.html", rows=rows)
|
||||
"""The fleet view folded into the Hosts hub — kept as a redirect so old
|
||||
links/widgets don't 404."""
|
||||
return redirect("/hosts/")
|
||||
|
||||
|
||||
@host_agent_bp.get("/widget")
|
||||
@@ -571,7 +570,14 @@ async def host_panel(host_id: str):
|
||||
|
||||
@host_agent_bp.get("/settings/")
|
||||
@require_role(UserRole.admin)
|
||||
async def settings_list():
|
||||
async def settings_redirect():
|
||||
"""Old management URL — agent management moved into the Hosts section."""
|
||||
return redirect(url_for("host_agent.fleet"))
|
||||
|
||||
|
||||
@host_agent_bp.get("/fleet/")
|
||||
@require_role(UserRole.admin)
|
||||
async def fleet():
|
||||
from steward.core.capabilities import has_capability
|
||||
from steward.models.ansible_inventory import AnsibleTarget, AnsibleGroup
|
||||
|
||||
@@ -641,7 +647,7 @@ async def add_host():
|
||||
await session.commit()
|
||||
host_id = host.id
|
||||
|
||||
return redirect(url_for("host_agent.settings_list") + f"?new_token={raw}&host_id={host_id}")
|
||||
return redirect(url_for("host_agent.fleet") + f"?new_token={raw}&host_id={host_id}")
|
||||
|
||||
|
||||
@host_agent_bp.post("/settings/<host_id>/rotate-token")
|
||||
@@ -657,7 +663,7 @@ async def rotate_token(host_id: str):
|
||||
reg.token_hash = hashed
|
||||
reg.token_created_at = datetime.now(timezone.utc)
|
||||
await session.commit()
|
||||
return redirect(url_for("host_agent.settings_list") + f"?new_token={raw}&host_id={host_id}")
|
||||
return redirect(url_for("host_agent.fleet") + f"?new_token={raw}&host_id={host_id}")
|
||||
|
||||
|
||||
@host_agent_bp.post("/settings/<host_id>/delete")
|
||||
@@ -670,7 +676,7 @@ async def delete_registration(host_id: str):
|
||||
if reg is not None:
|
||||
await session.delete(reg)
|
||||
await session.commit()
|
||||
return redirect(url_for("host_agent.settings_list"))
|
||||
return redirect(url_for("host_agent.fleet"))
|
||||
|
||||
|
||||
# ── Deploy via Ansible (plugin↔core synergy via the capability registry) ──────
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "_macros.html" import crumbs %}
|
||||
{% block title %}Host Agent — Steward{% endblock %}
|
||||
{% block breadcrumb %}{{ crumbs([("Host Agents", "/plugins/host_agent/"), ("Settings", "")]) }}{% endblock %}
|
||||
{% block title %}Agent fleet — Steward{% endblock %}
|
||||
{% block breadcrumb %}{{ crumbs([("Hosts", "/hosts/"), ("Agent fleet", "")]) }}{% endblock %}
|
||||
{% block content %}
|
||||
<h1 class="page-title">Host Agent — Registered Hosts</h1>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;gap:1rem;flex-wrap:wrap;">
|
||||
<h1 class="page-title" style="margin-bottom:0;">Agent fleet</h1>
|
||||
<a href="/hosts/" class="btn btn-ghost btn-sm">← Hosts</a>
|
||||
</div>
|
||||
<p style="color:var(--text-muted);font-size:0.84rem;margin:-0.4rem 0 1.25rem;">
|
||||
Bulk agent operations across your inventory. For a single host, use its
|
||||
<a href="/hosts/">host page</a> — provisioning and metrics live there.
|
||||
</p>
|
||||
|
||||
{% if new_token %}
|
||||
<div class="card" style="background:var(--accent-bg);">
|
||||
@@ -58,7 +65,7 @@
|
||||
No managed SSH key yet — Steward needs one to log into hosts as <code>steward</code>.
|
||||
</span>
|
||||
<form method="post" action="/settings/ansible/generate-key" style="margin:0;">
|
||||
<input type="hidden" name="next" value="/plugins/host_agent/settings/">
|
||||
<input type="hidden" name="next" value="/plugins/host_agent/fleet/">
|
||||
<button type="submit" class="btn btn-sm">Generate managed key</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user