mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +00:00
Under each host's CPU and memory load, the dialog shows one line: Given to VMs: 24 vCPUs (1.5:1), 48.0 GB memory. Pool default: 120.0 GB free. The line is read when the dialog opens, from computes/<id>/capacity/, as plain text, so the VM list itself reads no VM definitions. The CPU bars and the lines share one queue, host by host and two reads at a time, as each read holds a worker. A host that cannot be read says so. Compute.capacity now also gives the vCPUs per CPU and its pools with the default one first, so the Computes page and the dialog share them.
153 lines
8.1 KiB
HTML
153 lines
8.1 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
{% block title %}{% trans "Instances" %}{% endblock %}
|
|
{% block page_heading %}{% trans "Instances" %}{% endblock page_heading %}
|
|
|
|
{% block page_heading_extra %}
|
|
{% if request.user.is_superuser %}
|
|
{% include 'create_inst_block.html' %}
|
|
{% endif %}
|
|
{# the list's filters (state, text) and the address search, as one control #}
|
|
<div class="float-end search">
|
|
<div class="input-group">
|
|
<select id="state-filter" class="form-select flex-grow-0 w-auto" aria-label="{% trans 'State' %}">
|
|
<option value="">{% trans 'All states' %}</option>
|
|
{% for state in states %}
|
|
<option>{{ state }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<input id="filter" class="form-control" type="text" placeholder="{% trans 'Search' %}" aria-label="{% trans 'Search' %}">
|
|
<button type="button" class="btn btn-outline-secondary" id="find-address-open"
|
|
data-bs-toggle="modal" data-bs-target="#find-address">{% trans "Find IP" %}</button>
|
|
</div>
|
|
</div>
|
|
{% endblock page_heading_extra %}
|
|
|
|
{% block content %}
|
|
{% if show_hosts %}
|
|
{% for compute in computes %}
|
|
{% if compute.status is not True %}
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
{% trans 'Problem occurred with host' %} {{ compute.name }}: {{ compute.connection_error }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% elif own_hosts_down %}
|
|
<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
|
{% trans 'A host of some of your virtual machines cannot be reached right now.' %}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% endif %}
|
|
<div class="col-lg-12">
|
|
{% if app_settings.VIEW_INSTANCES_LIST_STYLE == 'grouped' and request.user.is_superuser %}
|
|
{% include 'allinstances_index_grouped.html' %}
|
|
{% else %}
|
|
{% include 'allinstances_index_nongrouped.html' %}
|
|
{% endif %}
|
|
</div>
|
|
<div class="modal fade" id="find-address" tabindex="-1" aria-labelledby="find-address-title" aria-hidden="true"
|
|
data-url="{% url 'instances:find_address' %}" data-failed="{% trans "The search did not go through. Try again." %}"
|
|
data-none="{% trans "No running VM reports this address." %}"
|
|
data-none-mac="{% trans "No VM has a network card with this MAC address." %}"
|
|
data-unregistered="{% trans "(not registered in the panel)" %}" data-conflict="{% trans "possible conflict" %}"
|
|
data-checked="{% trans "Running VMs checked: %(n)s." %}"
|
|
data-no-agent="{% trans "%(n)s had no connected guest agent; a static address on them cannot be seen." %}"
|
|
data-unread="{% trans "Guest agents not read (a paused VM, no answer in time, or an error): %(n)s." %}"
|
|
data-hosts-down="{% trans "Not reached: %(hosts)s." %}"
|
|
data-hosts-unfinished="{% trans "Not finished in time: %(hosts)s." %}"
|
|
data-read-at="{% trans "Read at %(time)s." %}"
|
|
data-some-unchecked="{% trans "Some of your VMs could not be checked." %}">
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title h5" id="find-address-title">{% trans "Find the VMs using an IP or MAC address" %}</h2>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="find-address-form" class="d-flex gap-2 mb-3">
|
|
<label class="visually-hidden" for="find-address-q">{% trans "IP address, network or MAC address" %}</label>
|
|
<input type="text" class="form-control" name="q" id="find-address-q" required
|
|
placeholder="192.0.2.10, 192.0.2.0/24 {% trans "or" %} 52:54:00:12:34:56">
|
|
<button type="submit" class="btn btn-primary">{% trans "Find" %}</button>
|
|
</form>
|
|
<div class="alert alert-danger d-none" role="alert" id="find-address-error"></div>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm d-none" id="find-address-results">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "Address" %}</th>
|
|
<th scope="col">{% trans "VM" %}</th>
|
|
<th scope="col">{% trans "MAC" %}</th>
|
|
<th scope="col">{% trans "Source" %}</th>
|
|
{% if show_hosts %}<th scope="col">{% trans "Host" %}</th>{% endif %}
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
<p class="small text-body-secondary mb-0" id="find-address-coverage"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock content %}
|
|
{% block script %}
|
|
<script src="{% static 'js/instance_filter.js' %}"></script>
|
|
<script src="{% static 'js/find_address.js' %}"></script>
|
|
{% if request.user.is_superuser %}
|
|
<script>
|
|
// The new-VM dialog's CPU bars and capacity lines, read when the
|
|
// dialog opens, two reads at a time: a CPU read holds a worker for
|
|
// a second, a capacity read while it reads the host's VMs. Answers
|
|
// of an earlier opening are ignored.
|
|
const addInstance = document.getElementById("AddInstance");
|
|
let opening = 0, queue = [], reading = 0;
|
|
|
|
function setCpuUsage(cell, percent, text) {
|
|
const meter = cell.querySelector(".progress");
|
|
cell.querySelector(".progress-bar").style.width = (percent || 0) + "%";
|
|
if (percent === null) {
|
|
meter.removeAttribute("aria-valuenow");
|
|
} else {
|
|
meter.setAttribute("aria-valuenow", percent);
|
|
}
|
|
cell.querySelector(".cpu-usage-value").textContent = text;
|
|
}
|
|
|
|
// data: the answer, or null when the read failed
|
|
function show(element, data) {
|
|
if (element.classList.contains("compute-capacity")) {
|
|
element.textContent = data === null ? "" : data.trim();
|
|
return;
|
|
}
|
|
const usage = data !== null && typeof data.cpudata === "number" ? Math.round(data.cpudata) : null;
|
|
setCpuUsage(element, usage, usage === null ? "—" : usage + "%");
|
|
}
|
|
|
|
function readNext() {
|
|
while (reading < 2 && queue.length) {
|
|
const element = queue.shift(), current = opening;
|
|
reading++;
|
|
$.get(element.dataset.url)
|
|
.done((data) => { if (current === opening) show(element, data); })
|
|
.fail(() => { if (current === opening) show(element, null); })
|
|
.always(() => { reading--; readNext(); });
|
|
}
|
|
}
|
|
|
|
addInstance.addEventListener("show.bs.modal", function () {
|
|
opening++;
|
|
addInstance.querySelectorAll(".cpu-usage").forEach((cell) => setCpuUsage(cell, null, "…"));
|
|
// host by host, as the list shows them
|
|
queue = Array.from(addInstance.querySelectorAll(".cpu-usage, .compute-capacity"));
|
|
readNext();
|
|
});
|
|
addInstance.addEventListener("hide.bs.modal", function () {
|
|
opening++;
|
|
queue = [];
|
|
});
|
|
</script>
|
|
{% endif %}
|
|
{% endblock script %}
|