1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/storages/templates/storage.html
catborise 50dcaba699 refactor(templates): the host pages' scripts load from static files
The host pages lose their last inline scripts: the overview's charts
(host_graphs.js), the New pool, New network and New interface dialogs
(pool_dialog.js, network_dialog.js, interface_dialog.js), a network's
and a network filter's XML editors and dialogs (network_page.js,
nwfilter_page.js, nwfilters_page.js), the secrets page's labels
(secrets_page.js, its texts from the view as usage_texts) and a pool's
page with the ISO upload (storage_page.js). Their URLs and messages are
data attributes of the elements they belong to; the upload reads its
token and address from its form. No template has an inline script or
an inline event handler any more.
2026-11-02 09:45:26 +03:00

219 lines
14 KiB
HTML

{% extends "base.html" %}
{% load i18n %}
{% load operations %}
{% load static %}
{% load bootstrap_icons %}
{% block title %}{{ compute }} - {% trans "Storage" %}: {{ pool }}{% endblock %}
{% block page_heading %}{{ compute }} - {% trans "Storage" %}: {{ pool }}{% endblock page_heading %}
{% block page_heading_extra %}
<div class="d-flex flex-wrap gap-2">
{% include 'create_stg_vol_block.html' %}
{# the pool's own actions: starting or stopping it, the others in a menu #}
<div class="btn-group" role="group" aria-label="{% blocktrans %}Actions for pool {{ pool }}{% endblocktrans %}">
{% if state %}
<form method="post" class="btn-group">{% csrf_token %}
<button type="submit" class="btn btn-outline-secondary" name="stop"
data-confirm="{% blocktrans with name=pool %}Stop pool {{ name }}? Its files stay, but until it is started again its volumes cannot be managed and VMs with disks in it may not start.{% endblocktrans %}">{% bs_icon 'stop-fill' %} {% trans "Stop" %}</button>
</form>
{% else %}
<form method="post" class="btn-group">{% csrf_token %}
<button type="submit" class="btn btn-outline-secondary" name="start">{% bs_icon 'play-fill' %} {% trans "Start" %}</button>
</form>
{% endif %}
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false" aria-label="{% trans "More actions" %}">{% bs_icon 'three-dots' %}</button>
<ul class="dropdown-menu dropdown-menu-end shadow">
<li><form method="post">{% csrf_token %}
{% if autostart %}
<button type="submit" class="dropdown-item" name="unset_autostart"
data-confirm="{% blocktrans with name=pool %}Disable autostart of pool {{ name }}? After the host restarts it stays stopped.{% endblocktrans %}">{% bs_icon 'toggle-off' %} {% trans "Disable autostart" %}</button>
{% else %}
<button type="submit" class="dropdown-item" name="set_autostart">{% bs_icon 'toggle-on' %} {% trans "Enable autostart" %}</button>
{% endif %}
</form></li>
{% if not state %}
<li><hr class="dropdown-divider"></li>
<li><form method="post">{% csrf_token %}
<button type="submit" class="dropdown-item text-danger" name="delete"
data-confirm="{% blocktrans with name=pool %}Delete pool {{ name }}? Only its definition goes; its volumes and their files stay.{% endblocktrans %}">{% bs_icon 'trash' %} {% trans "Delete pool" %}</button>
</form></li>
{% endif %}
</ul>
</div>
</div>
</div>
{% endblock page_heading_extra %}
{% block content %}
<div id="job-panel" aria-live="polite"></div>
<div class="row">
<div class="col-lg-12">
{% include "computes/host_nav.html" with section="storages" detail=True %}
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-12 col-md-6">
<div class="card h-100">
<div class="card-header">{% trans "Space" %}</div>
<div class="card-body">
{% if not state %}
<div class="text-body-secondary">{% trans "Unavailable while the pool is stopped." %}</div>
{% elif size is None %}
<div class="text-body-secondary">{% trans "The pool's space could not be read." %}</div>
{% elif not size %}
<div class="text-body-secondary">{% trans "The pool has no size." %}</div>
{% else %}
<div class="fs-4 lh-sm">{{ percent }} %</div>
<div class="small text-body-secondary">
{% blocktrans with used=used|filesizeformat size=size|filesizeformat %}{{ used }} used of {{ size }}{% endblocktrans %}
{# what the space is the space of: other pools may share it #}
{% if type == "dir" or type == "fs" or type == "netfs" %}· {% trans "the filesystem of its path" %}
{% elif type == "logical" %}· {% trans "its volume group" %}
{% elif type == "rbd" %}· {% trans "the Ceph cluster" %}{% endif %}
</div>
<div class="progress my-2" role="progressbar" style="--bs-progress-height: 0.3rem;" aria-label="{% trans "Space used" %}"
aria-valuenow="{{ percent }}" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar {% if percent > 90 %}bg-danger{% elif percent > 75 %}bg-warning{% endif %}" style="width: {{ percent }}%"></div>
</div>
<div class="small text-body-secondary">{% blocktrans with free=free|filesizeformat %}{{ free }} free{% endblocktrans %}</div>
{% endif %}
</div>
</div>
</div>
<div class="col-12 col-md-6">
<div class="card h-100">
<div class="card-header">{% trans "Pool" %}</div>
<ul class="list-group list-group-flush">
<li class="list-group-item py-1 d-flex justify-content-between gap-3">
<span class="text-body-secondary">{% trans "State" %}</span>
<span class="d-flex flex-wrap justify-content-end gap-1">
{% if state %}<span class="badge text-bg-success">{% trans "Active" %}</span>{% else %}<span class="badge text-bg-secondary">{% trans "Stopped" %}</span>{% endif %}
{% if status == "Running degraded" %}<span class="badge text-bg-warning">{% trans "Degraded" %}</span>{% endif %}
{% if autostart %}<span class="badge text-bg-light border">{% trans "Autostart" %}</span>{% endif %}
</span>
</li>
<li class="list-group-item py-1 d-flex justify-content-between gap-3">
<span class="text-body-secondary">{% trans "Type" %}</span><span>{{ type|default:"—" }}</span>
</li>
<li class="list-group-item py-1 d-flex justify-content-between gap-3">
<span class="text-body-secondary">{% trans "Path" %}</span>
<span class="font-monospace text-break text-end">{{ path|default:"—" }}</span>
</li>
</ul>
</div>
</div>
</div>
{% if not state %}
<div class="alert alert-secondary">
{% bs_icon 'info-circle' %} {% trans "This pool is stopped: its volumes cannot be listed or managed until it is started. Their files stay." %}
</div>
{% elif volumes %}
<div class="d-flex flex-wrap align-items-center gap-2 mb-2">
<h5 class="me-auto mb-0">{% trans "Volumes" %}</h5>
<input id="filter" class="form-control w-auto" type="search" placeholder="{% trans "Search volumes" %}" aria-label="{% trans "Search volumes" %}">
</div>
<div class="table-responsive">
<table class="table table-hover align-middle" data-sortable>
<thead>
<tr>
<th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Format" %}</th>
<th scope="col" class="d-none d-sm-table-cell" data-sort-method="number">{% trans "Capacity" %}</th>
<th scope="col" data-sort-method="none"><span class="visually-hidden">{% trans "Actions" %}</span></th>
</tr>
</thead>
<tbody class="searchable">
{% for volume in volumes %}
<tr data-search="{{ volume.name }}">
<td class="text-break">{{ volume.name }}</td>
<td><span class="badge text-bg-light border">{{ volume.type }}</span></td>
{# capacity: what the guest sees; allocated: what it takes on the storage now #}
<td class="d-none d-sm-table-cell" data-sort="{{ volume.size }}">
<div>{{ volume.size|filesizeformat }}</div>
<div class="small text-body-secondary">{% blocktrans with allocated=volume.allocation|filesizeformat %}{{ allocated }} allocated{% endblocktrans %}</div>
</td>
<td class="text-end text-nowrap">
<div class="btn-group" role="group" aria-label="{% blocktrans with name=volume.name %}Actions for {{ name }}{% endblocktrans %}">
{# an ISO image is uploaded, not cloned (storages.jobs.is_iso) #}
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#CloneVolume" data-volume="{{ volume.name }}"
aria-label="{% trans "Clone" %}"{% if volume.iso %} disabled{% endif %}>{% bs_icon 'files' %} <span class="d-none d-sm-inline">{% trans "Clone" %}</span></button>
<div class="btn-group">
{# fixed: the menu would be cut off by the table's scrolling box #}
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false"
data-bs-popper-config='{"strategy": "fixed"}' aria-label="{% trans "More actions" %}">{% bs_icon 'three-dots' %}</button>
<ul class="dropdown-menu dropdown-menu-end shadow">
<li><form method="post">{% csrf_token %}
<input type="hidden" name="volname" value="{{ volume.name }}">
<button type="submit" class="dropdown-item text-danger" name="del_volume"
data-confirm="{% blocktrans with name=volume.name %}Delete volume {{ name }}? Its file and data are removed; a volume a VM uses is not deleted.{% endblocktrans %}">{% bs_icon 'trash' %} {% trans "Delete" %}</button>
</form></li>
</ul>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{# a live region from the start: screen readers hear the message when the search empties the table #}
<div role="status">
<div id="no-match" class="text-center text-body-secondary py-4" hidden>{% trans "No volume matches the search." %}</div>
</div>
{# one dialog for every volume: the button that opens it names the volume #}
<div class="modal fade" id="CloneVolume" tabindex="-1" aria-labelledby="CloneVolumeLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="CloneVolumeLabel">{% trans "Clone image" %} <span class="font-monospace text-break" id="clone-image-name"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
</div>
<form method="post" aria-labelledby="CloneVolumeLabel">{% csrf_token %}{% submission_key %}
<div class="modal-body">
<input type="hidden" name="image" id="clone-image">
<div class="mb-3">
<label class="form-label" for="clone-name">{% trans "Name of the copy" %}</label>
<input type="text" class="form-control" name="name" id="clone-name" required pattern="[a-zA-Z0-9\.\-_]+" aria-describedby="clone-name-help">
<div class="form-text" id="clone-name-help">{% trans "Letters, digits, dot, dash and underscore." %}</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="convert" value="true" id="clone-convert">
<label class="form-check-label" for="clone-convert">{% trans "Convert it to another format" %}</label>
</div>
<div class="mt-3 d-none" id="clone-format-row">
<label class="form-label" for="clone-format">{% trans "Format" %}</label>
<select name="format" class="form-select" id="clone-format">
<option value="raw">{% trans "raw" %}</option>
<option value="qcow">{% trans "qcow" %}</option>
<option value="qcow2">{% trans "qcow2" %}</option>
</select>
</div>
<div class="form-check mt-3 d-none" id="clone-prealloc-row">
<input class="form-check-input" type="checkbox" name="meta_prealloc" value="true" id="clone-prealloc">
<label class="form-check-label" for="clone-prealloc">{% trans "Preallocate metadata (qcow2)" %}</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
<button type="submit" class="btn btn-primary" name="cln_volume">{% trans "Clone" %}</button>
</div>
</form>
</div>
</div>
</div>
{% else %}
<div class="text-center text-body-secondary py-4">{% trans "This pool has no volumes." %}</div>
{% endif %}
{% endblock %}
{% block script %}
<script src="{% static 'js/poll.js' %}"></script>
{% job_panel_script job_resource %}
{% if volumes %}<script src="{% static 'js/table_filter.js' %}"></script>{% endif %}
<script src="{% static 'js/storage_page.js' %}"></script>
{% endblock %}