1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00
webvirtcloud/instances/templates/allinstances.html
catborise e5c7dc338d fix(templates): the list pages search their rows as text, with one script
The list pages' search (filter-table.js) read what was typed as a
regular expression: a "(" or "[" stopped it with an error. It also
needed jQuery, while the instance list had its own script for the same
search. Both are now static/js/table_filter.js: plain text in any case,
the instance list's state list and host groups where a page has them,
remembered for a day as before. A group's header row is marked as one
(data-group-header) instead of being any row without a state.
2026-10-25 23:08:42 +03:00

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/table_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 %}