{% extends "base.html" %} {% from "_macros.html" import crumbs, status_dot, status_badge %} {% block title %}{{ host.name }} — Hosts — Steward{% endblock %} {% block breadcrumb %}{{ crumbs([("Hosts", "/hosts/"), (host.name, "")]) }}{% endblock %} {% block content %}

{{ host.name }}

{{ host.address or "no address — resolves by name" }}
Edit {% if session.user_role == 'admin' %}
{{ csrf_field() }}
{% endif %}
{# ── Alerts on this host: what is breaching, before anything else ─────────── #} {% if alerts %}

Alerts on this host

{% endif %} {# ── Metric anomalies: out of what is normal for this host at that hour ───── #} {% with back="/hosts/" ~ host.id, base_resource=host.name %}{% include "_anomaly_card.html" %}{% endwith %} {# ── Live vitals strip (host_agent fragment) — full width on top, polled ────── #}
{# ── Monitors + Agent, side by side on wide screens, stacked when narrow ────── #}
{# ── Monitors ─────────────────────────────────────────────────────────────── #}

Monitors

{% if session.user_role in ['operator', 'admin'] %} Add monitor {% endif %} {% if uptime %} uptime{% for w in ("24h", "7d", "30d") %}{{ " ·" if not loop.first }} {{ w }} {{ ("%.1f%%"|format(uptime[w])) if uptime[w] is not none else '—' }}{% endfor %} {% endif %}
{% if monitors %} {% for d in monitors %} {% set m = d.monitor %} {% set latest = d.latest %}
{{ status_dot(d.state) }} {{ m.name }}
{{ m.type|monitor_type }} · {{ m.target }} {% if d.state == 'down' %} Down{% if d.down_since %} {{ d.down_since|ago|replace(' ago', '') }}{% endif %}{% if d.down_reason %} · {{ d.down_reason }}{% endif %} {% endif %}
{% for _ in range([30 - d.heartbeat|length, 0]|max) %}{% endfor %} {% for h in d.heartbeat %}{% endfor %}
{% if latest is none %}— {% elif not latest.is_up %}{{ status_badge('down') }} {% elif latest.response_ms is not none %} {{ "%.0f"|format(latest.response_ms) }} ms {% else %}{{ status_badge('up') }}{% endif %}
{% endfor %} {% else %}

No monitors yet for this host.

{% endif %}
{# Monitors card #} {# ── Agent (host_agent management panel) ──────────────────────────────────── #}
Loading agent…
{# Monitors + Agent grid #} {# ── Ansible (full width), before Docker ───────────────────────────────────── #}

Ansible

{% if linked_target %}
{{ linked_target.name }} {{ linked_target.address }} {% if linked_target.groups %}
{% for grp in linked_target.groups %} {{ grp.name }} {% endfor %}
{% endif %}
Edit target
{{ csrf_field() }}
{% if ansible_sources %}
Run a playbook against this host
{{ csrf_field() }}
Advanced
{% endif %} {% else %}

No Ansible target linked. A target gives this host an SSH connection so Steward can run playbooks and provision the agent.

{% if linkable_targets %}
{{ csrf_field() }}
{% endif %}
{{ csrf_field() }}
{% endif %}
{# Ansible card #} {# ── Docker (docker plugin fragment; renders nothing if the host has none). After Ansible: the container list can run long, and Ansible beneath it was easy to miss (#5214). ─────────────────────────────────────────────────────── #} {% if "docker" in enabled_plugins %}
{% endif %} {# ── SNMP (renders nothing unless a configured SNMP device maps to this host) ── #} {% if "snmp" in enabled_plugins %}
{% endif %} {# ── Syslog (renders nothing unless an added syslog source maps to this host) ── #} {% if "syslog" in enabled_plugins %}
{% endif %} {% endblock %}