mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +00:00
The VM page polls the VM state every 5 s and, while its Stats tab is open, the statistics every 10 s; the compute overview polls the host graph every 5 s. All used setInterval: - A new request went out on schedule while the previous one was still open. When a host goes silent, a stats request waits up to 30-45 s, so each open page held several sync workers at once. - Each time the Stats tab was shown, the page made new charts and a new interval without stopping the old ones, and polling went on on the other tabs of the page. - Hidden pages kept polling at full rate. Measured in a browser against a test server (old code / new): Stats tab shown 4 times, 31 s on it: 12 / 2 requests; 31 s on another tab of the page: 12 / 0; page hidden for 31 s: 15 / 0, and the new code asks once right after the page is shown again; with 25 s answers, at most 15 / 1 requests open at once. static/js/poll.js repeats a request while the page is visible: the next one 5 or 10 s after the previous answer, none while hidden, one at once when shown again. The Stats tab makes its charts once and polls only while shown. The answer handler runs after the poller's own bookkeeping, so an error in it (a chart for a disk added meanwhile) does not stop the polling. A Node test of poll.js runs with the test suite when node is installed.
248 lines
9.8 KiB
HTML
248 lines
9.8 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
{% load bootstrap_icons %}
|
|
|
|
{% block title %}{% trans "Overview" %} - {{ compute.name }}{% endblock %}
|
|
|
|
{% block page_heading %}{{ compute.name }}{% endblock page_heading %}
|
|
|
|
{% block content %}
|
|
<div class="row">
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb shadow-sm">
|
|
<li class="breadcrumb-item" aria-current="page">
|
|
<span class="fw-bold">{% bs_icon 'laptop' %} {% trans "Overview" %}</span>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'instances' compute.id %}">{% bs_icon 'server' %} {% trans "Instances" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'storages' compute.id %}">{% bs_icon 'device-hdd' %} {% trans "Storages" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'networks' compute.id %}">{% bs_icon 'hdd-network' %} {% trans "Networks" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'interfaces' compute.id %}">{% bs_icon 'wifi' %} {% trans "Interfaces" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'nwfilters' compute.id %}">{% bs_icon 'filter' %} {% trans "NWFilters" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'virtsecrets' compute.id %}">{% bs_icon 'key' %} {% trans "Secrets" %}</a>
|
|
</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="shadow-sm">
|
|
<h3 class="page-header">{% trans "Basic details" %}</h3>
|
|
<dl class="mx-3 row">
|
|
<dt class="col-3">{% trans "Hostname" %}</dt>
|
|
<dd class="col-9">{{ hostname }}</dd>
|
|
<dt class="col-3">{% trans "Hypervisors" %}</dt>
|
|
<dd class="col-9">
|
|
<div class="dropdown">
|
|
{% for arch, hpv in hypervisor.items|slice:":4" %}
|
|
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="hpvArchDrop{{ forloop.counter0 }}" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
{{ arch }}
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="hpvArchDrop{{ forloop.counter0 }}">
|
|
{% for h in hpv %}
|
|
<a class="dropdown-item" href="#">{{ h }}</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="dropdown">
|
|
{% if hypervisor.items|length > 4 %}
|
|
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="hpvDrop{{ forloop.counter0 }}" data-bs-toggle="dropdown">
|
|
{{ hypervisor.items|slice:"4:"|length }} {% trans 'more' %}...
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="hpvDrop{{ forloop.counter0 }}" role="menu">
|
|
{% for arc in hypervisor.keys|slice:"4:" %}
|
|
<a class="dropdown-item" tabindex="-1" href="#">{{ arc }}</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</dd>
|
|
<dt class="col-3">{% trans "Emulator" %}</dt>
|
|
<dd class="col-9">{{ emulator }}</dd>
|
|
<dt class="col-3">{% trans "Version" %}</dt>
|
|
<dd class="col-9">
|
|
<span class="badge bg-secondary">{% trans 'Qemu' %} </span>
|
|
<span class="badge bg-primary">{{ version }}</span>
|
|
<span class="badge bg-secondary">{% trans 'Libvirt' %} </span>
|
|
<span class="badge bg-primary">{{ lib_version }}</span>
|
|
</dd>
|
|
<dt class="col-3">{% trans "Memory" %}</dt>
|
|
<dd class="col-9">{{ host_memory|filesizeformat }}</dd>
|
|
<dt class="col-3">{% trans "Architecture" %}</dt>
|
|
<dd class="col-9">{{ host_arch }}</dd>
|
|
<dt class="col-3">{% trans "Logical CPUs" %}</dt>
|
|
<dd class="col-9">{{ logical_cpu }}</dd>
|
|
<dt class="col-3">{% trans "Processor" %}</dt>
|
|
<dd class="col-9">{{ model_cpu }}</dd>
|
|
<dt class="col-3">{% trans "Connection" %}</dt>
|
|
<dd class="col-9">{{ uri_conn }}</dd>
|
|
<dt class="col-3">{% trans "Details" %}</dt>
|
|
<dd class="col-9">{{ compute.details }}</dd>
|
|
</dl>
|
|
|
|
<h3 class="page-header">{% trans "Performance" %}</h3>
|
|
<div class="mx-3 shadow-sm">
|
|
<div class="my-3 card border-success">
|
|
<div class="card-body">
|
|
<h5 class="card-title">
|
|
{% bs_icon 'arrow-right' %}
|
|
{% trans "CPU Utilization" %}
|
|
</h5>
|
|
<canvas id="cpuChart" width="735" height="160"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="my-3 card border-primary">
|
|
<div class="card-body">
|
|
<h5 class="card-title ">
|
|
{% bs_icon 'arrow-right'%} {% trans "RAM Utilization" %}
|
|
</h5>
|
|
<canvas id="memChart" width="735" height="160"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
{% block script %}
|
|
<script src="{% static "js/Chart.bundle.min.js" %}"></script>
|
|
<script src="{% static "js/poll.js" %}"></script>
|
|
<script>
|
|
|
|
var cpu_ctx = document.getElementById("cpuChart").getContext("2d");
|
|
var cpuChart = new Chart(cpu_ctx, {
|
|
type: 'line',
|
|
data: {
|
|
datasets : [{
|
|
label: 'Usage',
|
|
backgroundColor: "rgba(241,72,70,0.5)",
|
|
pointRadius: 2,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
legend: {
|
|
display: false
|
|
},
|
|
scales: {
|
|
xAxes:[{
|
|
offset: false,
|
|
ticks: {
|
|
beginAtZero: false,
|
|
autoSkip: true,
|
|
maxTicksLimit: 10,
|
|
maxRotation: 0,
|
|
minRotation: 0,
|
|
stepSize: 10,
|
|
},
|
|
}],
|
|
yAxes: [{
|
|
ticks: {
|
|
max: 100,
|
|
min: 0,
|
|
stepSize: 20,
|
|
callback: function(value, index, values) {
|
|
return value + ' %';
|
|
}
|
|
},
|
|
}],
|
|
},
|
|
tooltips: {
|
|
callbacks: {
|
|
label: function (tooltipItem, chart) {
|
|
var label = chart.datasets[tooltipItem.datasetIndex].label || '';
|
|
if (label) {
|
|
label += ': ';
|
|
}
|
|
return label += tooltipItem.yLabel + ' %';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
var mem_ctx = document.getElementById("memChart").getContext("2d");
|
|
var memChart = new Chart(mem_ctx, {
|
|
type: 'line',
|
|
data: {
|
|
datasets: [{
|
|
pointRadius: 2,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
legend: {
|
|
display: false
|
|
},
|
|
scales: {
|
|
xAxes:[{
|
|
offset: false,
|
|
ticks: {
|
|
beginAtZero: false,
|
|
autoSkip: true,
|
|
maxTicksLimit: 10,
|
|
maxRotation: 0,
|
|
minRotation: 0
|
|
}
|
|
}],
|
|
yAxes: [{
|
|
ticks:{
|
|
suggestedMin: 0,
|
|
suggestedMax: 100,
|
|
callback: function(value, index, values) {
|
|
return value + ' MB';
|
|
}
|
|
},
|
|
}],
|
|
},
|
|
tooltips: {
|
|
callbacks: {
|
|
label: function (tooltipItem, chart) {
|
|
var label = chart.datasets[tooltipItem.datasetIndex].label || '';
|
|
if (label) {
|
|
label += ': ';
|
|
}
|
|
return label += tooltipItem.yLabel + ' MB';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (Boolean({{ status }}) === true) {
|
|
pollWhileVisible(5000, function () {
|
|
return $.getJSON('{% url 'compute_graph' compute_id %}');
|
|
}, function graph_usage(data) {
|
|
cpuChart.data.labels.push(data.timeline);
|
|
memChart.data.labels.push(data.timeline);
|
|
|
|
cpuChart.data.datasets[0].data.push(data.cpudata);
|
|
if (cpuChart.data.datasets[0].data.length > 10){
|
|
cpuChart.data.labels.shift();
|
|
cpuChart.data.datasets[0].data.shift();
|
|
}
|
|
memChart.options.scales.yAxes[0].ticks.max = parseInt(data.memdata.total / 1048576);
|
|
memChart.options.scales.yAxes[0].ticks.stepSize = parseInt(data.memdata.total / (1048576 * 5));
|
|
memChart.data.datasets[0].data.push(parseInt(data.memdata.usage / 1048576));
|
|
|
|
if (memChart.data.datasets[0].data.length > 10){
|
|
memChart.data.labels.shift();
|
|
memChart.data.datasets[0].data.shift();
|
|
}
|
|
|
|
cpuChart.update();
|
|
memChart.update();
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|