A verdict line (services healthy · other containers running · failed in 24h), then "Needs a look": troubled services with why (crash-looping, N of M running), since when and the last error, and troubled containers (unhealthy, restarting, exited with an error, out of memory). Then "Failed in the last 24h" by name, with how often, when and where. Then the healthy rest one line each (name · hosts · replicas) in as many columns as the widget is wide, collapsing after 40 to "+N more healthy". Per-container CPU is gone from this widget: Busiest Containers owns it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
4.2 KiB
HTML
81 lines
4.2 KiB
HTML
{# docker/widget.html — the dashboard Docker widget (#5252), in the widget-shape
|
|
order: a verdict line; the services and containers in trouble; what failed
|
|
in the last 24h, by name; then everything healthy, one line each, packed
|
|
into as many columns as the widget is wide. Resource use is the Busiest
|
|
Containers widget's, so it isn't repeated here. #}
|
|
{% from "_macros.html" import status_dot %}
|
|
{% if total_count == 0 %}
|
|
<div class="w-verdict" style="color:var(--text-muted);">No containers reported yet.</div>
|
|
{% else %}
|
|
{% set wrong = bad_services|length + bad_containers|length %}
|
|
<div class="w-verdict {{ 'tone-crit' if wrong else ('tone-warn' if failures else 'tone-ok') }}">
|
|
{%- if services_total %}{{ services_healthy }}/{{ services_total }} services healthy · {% endif -%}
|
|
{{ running_count }} other container{{ 's' if running_count != 1 }} running ·
|
|
{% if failures %}<a href="/plugins/docker/#failures" style="color:inherit;">{{ failures|length }} failed in 24h</a>{% else %}nothing failed in 24h{% endif -%}
|
|
</div>
|
|
|
|
{% if wrong %}
|
|
<div class="w-section">Needs a look</div>
|
|
{% for s in bad_services %}
|
|
<div class="w-item">
|
|
{{ status_dot('degraded' if s.state == 'degraded' else 'down') }}
|
|
<span class="w-label" title="{{ s.name }}">{{ s.name }}
|
|
<span class="w-sub {{ 'tone-crit' if s.state != 'degraded' else 'tone-warn' }}">
|
|
{%- if s.crash_looping %}crash-looping: {{ s.failures_1h }} failed in the last hour{% else %}{{ s.running }} of {{ s.desired }} running{% endif %}
|
|
{%- if s.trouble_since %}, since {{ s.trouble_since|ago }}{% endif %}
|
|
{%- if s.last_error and (s.last_error.error or s.last_error.message) %} · {{ s.last_error.error or s.last_error.message }}{% endif -%}
|
|
</span>
|
|
</span>
|
|
<span class="w-meta" title="replicas running / wanted">{{ s.running }}/{{ s.desired }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% for c in bad_containers %}
|
|
<div class="w-item">
|
|
{{ status_dot('warn' if c.status == 'restarting' else 'down') }}
|
|
<span class="w-label" title="{{ c.name }}">
|
|
{%- if session.user_id %}<a href="/plugins/docker/container/{{ c.host_id }}/{{ c.name }}">{{ c.name }}</a>{% else %}{{ c.name }}{% endif %}
|
|
<span class="w-sub tone-crit">
|
|
{%- if c.health == 'unhealthy' %}unhealthy{% elif c.status == 'restarting' %}restarting{% else %}exited {{ c.exit_code }}{% if c.oom_killed %}, out of memory{% endif %}{% endif %} · {{ host_name.get(c.host_id, c.host_id) }}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
{% if failures %}
|
|
<div class="w-section">Failed in the last 24h</div>
|
|
{% for f in failures[:5] %}
|
|
<div class="w-item">
|
|
{{ status_dot('failed') }}
|
|
<span class="w-label" title="{{ f.name }}">{{ f.name }}
|
|
<span class="w-sub">{% if f.count > 1 %}{{ f.count }} times, last {% endif %}{{ f.at|ago }}{% if f.detail %} · {{ f.detail }}{% endif %} · {{ f.host }}</span>
|
|
</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% if failures|length > 5 %}<div class="w-more">+{{ failures|length - 5 }} more · <a href="/plugins/docker/#failures">the full list</a></div>{% endif %}
|
|
{% endif %}
|
|
|
|
{% if healthy %}
|
|
<div class="w-section">Healthy</div>
|
|
<div class="w-grid">
|
|
{% for h in healthy %}
|
|
<div class="w-item">
|
|
{% if h.kind == 'service' %}{% set s = h.s %}
|
|
{{ status_dot('up') }}
|
|
<span class="w-label" title="{{ s.name }} — on {{ s.replicas|map(attribute='host')|unique|join(', ') }}">{{ s.name }}
|
|
<span style="color:var(--text-dim);"> · {{ s.replicas|map(attribute='host')|unique|join(', ') }}</span></span>
|
|
<span class="w-meta" title="replicas running / wanted">{{ s.running }}/{{ s.desired }}</span>
|
|
{% else %}{% set c = h.c %}
|
|
{{ status_dot('up' if h.kind == 'container' else 'unknown') }}
|
|
<span class="w-label" title="{{ c.name }}">
|
|
{%- if session.user_id %}<a href="/plugins/docker/container/{{ c.host_id }}/{{ c.name }}">{{ c.name }}</a>{% else %}{{ c.name }}{% endif %}
|
|
<span style="color:var(--text-dim);"> · {{ host_name.get(c.host_id, c.host_id) }}</span></span>
|
|
{% if h.kind == 'stopped' %}<span class="w-meta">stopped</span>{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% if healthy_more %}<div class="w-more">+{{ healthy_more }} more healthy · <a href="/plugins/docker/">all of Docker</a></div>{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|