Each figure after its bars takes a fixed width, so memory figures of different lengths no longer shift their bars. CPU and Memory headers say "last 6h" and the note gives the slice length. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
3.5 KiB
HTML
52 lines
3.5 KiB
HTML
{# docker/widget_resources.html — the dashboard Busiest Containers widget
|
|
(#5253, #5214): a verdict naming the busiest, then the top containers by CPU
|
|
(averaged over the last few minutes, so the order holds still). Each figure
|
|
carries its unit, beside bars of the last few hours. A row's bars are scaled
|
|
to its own peak, but never below a floor (1 core, 1 GiB by default), so a
|
|
container idling at 0.05 cores reads flat and a spike means real load. #}
|
|
{% macro cores(v) -%}{{ "%.2f"|format(v) if v < 1 else "%.1f"|format(v) }}{%- endmacro %}
|
|
{% macro bars(b, peak_text, roof_text) -%}
|
|
<span class="w-bars w-wide-only" aria-hidden="true"
|
|
title="{% if b.peak is none %}No samples in the last {{ hours }}h{% else %}Peak {{ peak_text }} in the last {{ hours }}h; bars reach the top at {{ roof_text }}{% endif %}">
|
|
{%- for h in b.bars %}<i{% if h is none %} class="gap"{% else %} style="height:{{ [h, 4]|max }}%"{% endif %}></i>{% endfor -%}
|
|
</span>
|
|
{%- endmacro %}
|
|
{% if not rows %}
|
|
<div class="w-verdict" style="color:var(--text-muted);">No running containers.</div>
|
|
{% else %}
|
|
{% set top = rows[0] %}
|
|
<div class="w-verdict">
|
|
Busiest: <strong>{{ top.label }}</strong> on {{ top.host_name }},
|
|
{% if top.usage.cores is not none %}{{ cores(top.usage.cores) }}
|
|
{%- if top.usage.cpus %} of {{ top.usage.cpus }} cores{% else %} cores{% endif %}{% else %}no CPU reading yet{% endif %}.
|
|
</div>
|
|
<table class="w-table">
|
|
<colgroup><col><col class="w-wide-only" style="width:7.5rem"><col class="w-barcol"><col class="w-barcol"></colgroup>
|
|
<thead><tr><th>Container</th><th class="w-wide-only w-text">Host</th>
|
|
<th title="Cores, averaged over the last {{ minutes }} minutes; bars: the last {{ hours }}h">CPU<span class="w-wide-only"> · last {{ hours }}h</span></th>
|
|
<th title="In use now; bars: the last {{ hours }}h">Memory<span class="w-wide-only"> · last {{ hours }}h</span></th></tr></thead>
|
|
<tbody>
|
|
{% for r in rows %}
|
|
{% set u = r.usage %}
|
|
<tr>
|
|
<td class="w-name" title="{{ r.c.name }}">
|
|
{%- if session.user_id %}<a href="/plugins/docker/container/{{ r.c.host_id }}/{{ r.c.name }}">{{ r.label }}</a>{% else %}{{ r.label }}{% endif -%}
|
|
</td>
|
|
<td class="w-wide-only w-text" title="{{ r.host_name }}">{{ r.host_name }}</td>
|
|
<td class="tone-{{ status_class(u.cpu_share, 'cpu') if u.cpus and u.cores is not none else 'dim' }}"
|
|
{% if u.cpus and u.cores is not none %}title="{{ '%.1f'|format(u.cpu_share) }}% of {{ r.host_name }}'s {{ u.cpus }} cores"{% endif %}>
|
|
{{- bars(r.cpu_bars, (cores(r.cpu_bars.peak) ~ " cores") if r.cpu_bars.peak is not none else "", cores(r.cpu_bars.roof) ~ " cores") -}}
|
|
<span class="w-fig">{% if u.cores is none %}—{% else %}{{ cores(u.cores) }} cores{% endif %}</span>
|
|
</td>
|
|
<td class="tone-{{ status_class(u.mem_pct, 'mem') if u.mem_of and u.mem_bytes is not none else 'dim' }}"
|
|
{% if u.mem_pct is not none and u.mem_of and u.mem_bytes is not none %}title="{{ '%.1f'|format(u.mem_pct) }}% of {{ u.mem_of }}"{% endif %}>
|
|
{{- bars(r.mem_bars, r.mem_bars.peak|bytes if r.mem_bars.peak is not none else "", r.mem_bars.roof|bytes) -}}
|
|
<span class="w-fig">{% if u.mem_bytes is none %}—{% else %}{{ u.mem_bytes|bytes }}{% endif %}</span>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
<div class="w-note w-wide-only">Bars: {{ (hours * 60 // 24) }}-minute slices, oldest left; each row up to its own peak, at least {{ cores(cpu_floor) }} core{{ 's' if cpu_floor != 1 }} and {{ mem_floor|bytes }}.</div>
|
|
{% endif %}
|