mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +00:00
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.
254 lines
13 KiB
HTML
254 lines
13 KiB
HTML
{% 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" %} →</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" %} →</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">–</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">–</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 %}
|