1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00
webvirtcloud/computes/templates/overview.html
catborise c1e30aeb7f refactor(templates): Bootstrap's components instead of the panel's own CSS for capacity, steps and disclosures
The fork's own rules in webvirtcloud.css redid what Bootstrap 5.3 has:
- the capacity bars (.cap-*) are Bootstrap progress bars: the memory a
  .progress-stacked of running VMs' memory and shut-off VMs' (at half
  opacity), each segment a progressbar with its amount as its value
  text; the pool bars a thin .progress. The line where the host's
  memory ends goes: the share above the bar says it, red when over;
- the status dot (.compute-dot) is a rounded-circle, the big figures
  (.compute-ratio) fs-4, the Computes table's cell padding py-3;
- the steps of creating a VM (.create-steps) are a breadcrumb with "›"
  as its divider;
- the XML and Advanced disclosures (.disclosure with a turning chevron)
  are Bootstrap accordions;
- the chosen volumes and networks (.row-list) are list-unstyled, their
  names text-break;
- a chart's container is position-relative by utility; its height stays.
Dead rules go too: .netIN, .netOUT, .diskRD, .diskWR, .input-xs and
.bottom-bar-margin had no users.
2026-10-27 17:05:03 +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="d-inline-block rounded-circle p-1 flex-shrink-0 bg-success" aria-hidden="true"></span> <span class="text-body">{% trans "Connected" %}</span>
{% else %}
<span class="d-inline-block rounded-circle p-1 flex-shrink-0 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="fs-4 lh-sm">{{ 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">
{% 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">
{% 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 position-relative"><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 position-relative"><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 %}