1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 19:56:48 +00:00
webvirtcloud/computes/templates/overview.html
catborise 1284b93705 feat(computes): memory given as a share; the overview in Bootstrap cards and lists
The memory given read "24.0 GB given of 32.0 GB": long, and the share
of the host's memory, what one looks for, had to be worked out. It is
now told as the vCPUs are: the share large ("75 %", amber over 90 %,
red over 100 %), then "24.0 GB / 32.0 GB", the bar and "12.0 GB
running". The Computes list's memory column shares it.

Every card of the overview has a Bootstrap card-header with an icon:
VMs, vCPUs, Memory, Storage, CPU in use and Memory in use (with the
current reading at its right), Hardware, Software and connection. The
last two were label and value lists under indented headings; they are
list-group-flush rows now, the label at the left, the value at the
right: counts in semibold, the hypervisor's and libvirt's versions as
badges, the guest architectures as light badges, the emulator and the
connection as code. The section headings, indented by the panel's
page-header margin unlike the cards, are gone: each card names itself.
The status line and notes are not paragraphs, which the panel indents.
2026-10-27 16:32:39 +03:00

254 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load bootstrap_icons %}
{% block title %}{% trans "Overview" %} - {{ compute.name }}{% endblock %}
{% block page_heading_extra %}
<a class="btn btn-sm btn-outline-primary" href="{% url 'compute_update' compute.id %}">{% bs_icon 'pencil' %} {% trans "Edit connection" %}</a>
<a class="btn btn-sm btn-outline-danger" href="{% url 'compute_delete' compute.id %}">{% bs_icon 'x-circle' %} {% trans "Remove from panel" %}</a>
{% endblock page_heading_extra %}
{% block page_heading %}{{ compute.name }}{% endblock page_heading %}
{% block content %}
<div id="host_status" class="d-flex flex-wrap align-items-baseline column-gap-3 text-body-secondary mb-3">
<span>
{% if connected %}
<span class="compute-dot bg-success" aria-hidden="true"></span> <span class="text-body">{% trans "Connected" %}</span>
{% else %}
<span class="compute-dot bg-danger" aria-hidden="true"></span> <span class="text-body">{% trans "Not connected" %}</span>
{% endif %}
</span>
<span class="text-break">{{ compute.hostname }}</span>
{% if compute.details %}<span class="text-break">{{ compute.details }}</span>{% endif %}
</div>
{% include "computes/host_nav.html" with section="overview" %}
{% if not connected %}
<div class="alert alert-warning" role="alert">
<div class="mb-2">{% trans "The panel cannot reach this host, so its VMs, capacity and load are not shown:" %} {{ compute.connection_error }}</div>
<a class="btn btn-sm btn-warning" href="{% url 'overview' compute.id %}">{% bs_icon 'arrow-clockwise' %} {% trans "Try again" %}</a>
<a class="btn btn-sm btn-outline-secondary" href="{% url 'compute_update' compute.id %}">{% trans "Edit connection" %}</a>
</div>
{% else %}
{% if read_error %}
<div class="alert alert-warning" role="alert">{% trans "The host's details could not be read:" %} {{ read_error }}</div>
{% endif %}
{% if given %}
<div class="row row-cols-1 row-cols-sm-2 row-cols-xl-4 g-3 mb-4">
<div class="col">
<div class="card h-100">
<div class="card-header">{% bs_icon 'server' %} {% trans "VMs" %}</div>
<div class="card-body">
<div class="compute-ratio">{{ given.vm_count }}</div>
<div class="small text-body-secondary">
{% blocktrans with running=given.vms.running shutoff=given.vms.shutoff %}{{ running }} running, {{ shutoff }} shut off{% endblocktrans %}{% if given.vms.paused %}, {% blocktrans with paused=given.vms.paused %}{{ paused }} paused{% endblocktrans %}{% endif %}{% if given.vms.other %}, {% blocktrans with other=given.vms.other %}{{ other }} other{% endblocktrans %}{% endif %}
</div>
<a class="small" href="{% url 'instances' compute.id %}">{% trans "Instances" %} &rarr;</a>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-header">{% bs_icon 'cpu' %} {% trans "vCPUs" %}</div>
<div class="card-body">
{% include "computes/capacity_vcpus.html" %}
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-header">{% bs_icon 'memory' %} {% trans "Memory" %}</div>
<div class="card-body compute-memory">
{% include "computes/capacity_memory.html" %}
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-header">{% bs_icon 'device-hdd' %} {% trans "Storage" %}</div>
<div class="card-body compute-storage">
{% include "computes/capacity_storage.html" %}
<a class="small" href="{% url 'storages' compute.id %}">{% trans "Storages" %} &rarr;</a>
</div>
</div>
</div>
</div>
{% else %}
<div class="text-body-secondary mb-4">{% trans "The capacity could not be read." %}</div>
{% endif %}
{# the load is read on its own: shown also when the details could not be #}
<div class="row g-3">
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between">
<span>{% bs_icon 'speedometer2' %} {% trans "CPU in use" %}</span> <span id="cpu_now" class="fw-semibold">&ndash;</span>
</div>
<div class="card-body">
<div class="chart"><canvas id="cpuChart" role="img" aria-label="{% trans "CPU in use, recent readings" %}"></canvas></div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between">
<span>{% bs_icon 'memory' %} {% trans "Memory in use" %}</span> <span id="mem_now" class="fw-semibold">&ndash;</span>
</div>
<div class="card-body">
<div class="chart"><canvas id="memChart" role="img" aria-label="{% trans "Memory in use, recent readings" %}"></canvas></div>
</div>
</div>
</div>
</div>
<div class="small text-body-secondary mt-2 mb-4">
{% trans "The last 10 readings, one every 5 seconds. Memory in use leaves out the host's buffers and cache." %}
<span id="graph_state" data-updated="{% trans "Updated at %(time)s." %}"
data-stale="{% trans "Not updated since %(time)s: the host did not answer." %}">{% trans "Waiting for the first reading." %}</span>
</div>
{% if host %}
<div class="row g-3 mb-4">
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">{% bs_icon 'cpu' %} {% trans "Hardware" %}</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Processor" %}</span>
<span class="text-end text-break">{{ host.processor|default:"–" }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Architecture" %}</span>
<span class="text-end text-break">{{ host.arch }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Logical CPUs" %}</span>
<span class="text-end text-break"><span class="fw-semibold">{{ host.topology.cpus }}</span></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Physical cores" %}</span>
<span class="text-end text-break"><span class="fw-semibold">{{ host.topology.cores|default:"–" }}</span></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Sockets" %}</span>
<span class="text-end text-break"><span class="fw-semibold">{{ host.topology.sockets|default:"–" }}</span></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "NUMA nodes" %}</span>
<span class="text-end text-break"><span class="fw-semibold">{{ host.topology.nodes|default:"–" }}</span></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Clock" %}</span>
<span class="text-end text-break">{{ host.ghz }} GHz</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Memory" %}</span>
<span class="text-end text-break"><span class="fw-semibold">{{ host.memory|filesizeformat }}</span></span>
</li>
</ul>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">{% bs_icon 'layers' %} {% trans "Software and connection" %}</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Hostname" %}</span>
<span class="text-end text-break">{{ host.hostname }}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{{ host.hypervisor }}</span>
<span class="text-end text-break"><span class="badge text-bg-primary">{{ host.hypervisor_version }}</span></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">libvirt</span>
<span class="text-end text-break"><span class="badge text-bg-primary">{{ host.libvirt_version }}</span></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Guest architectures" %}</span>
<span class="text-end text-break">{% for arch, types in host.guest_archs.items %}<span class="badge text-bg-light border ms-1">{{ arch }} <span class="fw-normal text-body-secondary">{{ types|join:" · " }}</span></span>{% endfor %}</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Emulator" %}</span>
<span class="text-end text-break"><code>{{ host.emulator|default:"–" }}</code></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-baseline gap-3">
<span class="text-body-secondary">{% trans "Connection" %}</span>
<span class="text-end text-break"><code>{{ host.uri }}</code></span>
</li>
</ul>
</div>
</div>
</div>
{% endif %}
{% endif %}
{% endblock %}
{% block script %}
{% if connected %}
<script src="{% static "js/chart.umd.min.js" %}"></script>
<script src="{% static "js/charts.js" %}"></script>
<script src="{% static "js/poll.js" %}"></script>
<script>
{% trans "average over" as average_over %}
// the seconds each CPU point averages
var cpuWindows = [];
var cpuChart = lineChart(document.getElementById("cpuChart"), "%", ["Usage"], {
max: 100,
tooltip: function (context, text) {
var span = cpuWindows[context.dataIndex];
return span ? text + ' ({{ average_over|escapejs }} ' + span + ' s)' : text;
},
});
var memChart = lineChart(document.getElementById("memChart"), "GB", ["Usage"]);
var cpuNow = document.getElementById("cpu_now");
var memNow = document.getElementById("mem_now");
var state = document.getElementById("graph_state");
var lastUpdate = "–";
function gb(bytes) {
return Math.round(bytes / 107374182.4) / 10;
}
// a failed reading leaves the charts as they were: say since when
function stale() {
state.textContent = state.dataset.stale.replace("%(time)s", lastUpdate);
state.classList.add("text-danger");
}
// the CPU sample of the previous answer: usage is taken since then
var cpuSample = null;
pollWhileVisible(5000, function () {
return $.getJSON('{% url 'compute_graph' compute.id %}', cpuSample ? {cpu_sample: cpuSample} : {}).fail(stale);
}, function (data) {
cpuSample = data.cpu_sample || null;
cpuWindows.push(data.window);
if (cpuWindows.length > CHART_POINTS) {
cpuWindows.shift();
}
addPoint(cpuChart, data.timeline, [data.cpudata]);
// a host that could not be read gives no values: a gap, same axis
if (data.memdata.total) {
memChart.options.scales.y.max = gb(data.memdata.total);
addPoint(memChart, data.timeline, [gb(data.memdata.usage)]);
memNow.textContent = gb(data.memdata.usage) + " / " + gb(data.memdata.total) + " GB";
} else {
addPoint(memChart, data.timeline, [null]);
}
if (data.cpudata == null) {
stale();
return;
}
cpuNow.textContent = data.cpudata + " %";
lastUpdate = data.timeline;
state.textContent = state.dataset.updated.replace("%(time)s", data.timeline);
state.classList.remove("text-danger");
}, 0);
</script>
{% endif %}
{% endblock %}