mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
feat(instances): a VM's disks as rows of one list
The Disks tab gave each disk a card: its capacity in large type, its pool, its path and its buttons stacked, two cards to a row, so a VM with a few disks filled the screen. Each disk is now a row of one list: the device with its bus and format, the capacity and the space used with a thin bar, the pool and the path, and Edit, Move, Detach and Delete at the row's end; the device details still fold out below it. A qcow2 file whose metadata takes it past its capacity shows a full bar.
This commit is contained in:
parent
488a77b3c7
commit
68d0631e33
2 changed files with 44 additions and 38 deletions
|
|
@ -145,7 +145,7 @@
|
|||
</section>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="disks">
|
||||
{# its CD-ROM drives, then its disks as cards; the forms are those of the actions #}
|
||||
{# its CD-ROM drives, then its disks as list rows; the forms are those of the actions #}
|
||||
{% if instance.status != 5 %}
|
||||
<p class="small text-body-secondary">{% bs_icon 'lock' %} {% trans "The VM is running: CD-ROM drives added now use the SCSI bus. Shut it down to detach other CD-ROM drives, or to detach or delete disks." %}</p>
|
||||
{% endif %}
|
||||
|
|
@ -203,41 +203,34 @@
|
|||
<h2 class="h6 mb-0" id="disks-heading">{% trans "Disks" %} <span class="text-body-secondary fw-normal">· {{ instance.config_disks|length }}</span></h2>
|
||||
{% include 'add_instance_volume.html' %}
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="card">
|
||||
<div class="list-group list-group-flush">
|
||||
{% for disk in instance.config_disks %}
|
||||
<div class="col-12{% if instance.config_disks|length > 1 %} col-md-6{% endif %}">
|
||||
<article class="card h-100" aria-labelledby="disk-{{ disk.dev }}">
|
||||
<div class="card-body small">
|
||||
<div class="d-flex justify-content-between align-items-center gap-2 mb-2">
|
||||
<h3 class="h6 mb-0" id="disk-{{ disk.dev }}">{% bs_icon 'device-hdd' %} {{ disk.dev }}</h3>
|
||||
<span class="text-body-secondary">{{ disk.bus }} · {{ disk.format }}</span>
|
||||
</div>
|
||||
<article class="list-group-item py-3" aria-labelledby="disk-{{ disk.dev }}">
|
||||
<div class="row g-3 align-items-start small">
|
||||
<div class="col-12 col-md-2">
|
||||
<h3 class="h6 mb-1" id="disk-{{ disk.dev }}">{% bs_icon 'device-hdd' %} {{ disk.dev }}</h3>
|
||||
<div class="text-body-secondary">{{ disk.bus }} · {{ disk.format }}</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
{% if disk.storage is None %}
|
||||
<div class="alert alert-danger mb-0">{% trans "Error getting disk info" %}</div>
|
||||
{% else %}
|
||||
<div class="text-body-secondary">{% trans "Capacity" %}</div>
|
||||
<p class="fs-4 fw-semibold lh-sm mb-0">{{ disk.size|filesizeformat }}</p>
|
||||
<p class="text-body-secondary mb-2">{% blocktrans with used=disk.used|filesizeformat %}{{ used }} used{% endblocktrans %}</p>
|
||||
<div class="border-top pt-2">
|
||||
<div class="mb-1"><span class="text-body-secondary">{% trans "Storage pool" %}</span> <strong class="ms-1">{{ disk.storage }}</strong></div>
|
||||
<div class="text-body-secondary">{% trans "Source" %}</div>
|
||||
<p class="text-break mb-2">{{ disk.path }}</p>
|
||||
{% widthratio disk.used disk.size 100 as used_percent %}
|
||||
{# a qcow2 file's metadata can take it past its capacity: the bar stops full #}
|
||||
{% if used_percent|add:0 > 100 %}{% widthratio 100 1 1 as used_percent %}{% endif %}
|
||||
<div class="d-flex flex-wrap justify-content-between gap-1">
|
||||
<strong>{% trans "Capacity" %} {{ disk.size|filesizeformat }}</strong>
|
||||
<span class="text-body-secondary">{% blocktrans with used=disk.used|filesizeformat %}{{ used }} used{% endblocktrans %}</span>
|
||||
</div>
|
||||
<div class="progress my-2" style="height: .25rem;" role="progressbar" aria-label="{% blocktrans with dev=disk.dev %}{{ dev }} used{% endblocktrans %}" aria-valuenow="{{ used_percent }}" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="progress-bar" style="width: {{ used_percent }}%"></div>
|
||||
</div>
|
||||
<div><span class="text-body-secondary">{% trans "Storage pool" %}</span> <strong>{{ disk.storage }}</strong></div>
|
||||
<div class="text-body-secondary text-break"><span class="visually-hidden">{% trans "Source" %}: </span>{{ disk.path }}</div>
|
||||
{% endif %}
|
||||
<details>
|
||||
<summary class="text-body-secondary">{% trans "Device details" %}</summary>
|
||||
<dl class="row mb-0 mt-2" style="max-width: 32rem">
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Cache" %}</dt><dd class="col-6 mb-1">{{ disk.cache }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "IO mode" %}</dt><dd class="col-6 mb-1">{{ disk.io }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Discard" %}</dt><dd class="col-6 mb-1">{{ disk.discard }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Detect zeroes" %}</dt><dd class="col-6 mb-1">{{ disk.detect_zeroes }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Serial" %}</dt><dd class="col-6 mb-1">{{ disk.serial|default:"—" }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Read-only" %}</dt><dd class="col-6 mb-1">{{ disk.readonly|yesno }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Shareable" %}</dt><dd class="col-6 mb-0">{{ disk.shareable|yesno }}</dd>
|
||||
</dl>
|
||||
</details>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent d-flex flex-wrap gap-2">
|
||||
<div class="col-12 col-md-4 d-flex flex-wrap justify-content-md-end gap-2">
|
||||
<form class="d-inline" action="{% url 'instances:edit_volume' instance.id %}" method="post" aria-label="{% trans "Edit instance volume form" %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="path" value="{{ disk.path }}">
|
||||
|
|
@ -264,10 +257,23 @@
|
|||
<button type="submit" class="btn btn-sm btn-danger"{% if instance.status != 5 %} disabled{% endif %} data-confirm="{% blocktrans with dev=disk.dev %}Delete {{ dev }}? It is detached and its file and data are removed.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)">{% trans "Delete" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<details class="small mt-2">
|
||||
<summary class="text-body-secondary">{% trans "Device details" %}</summary>
|
||||
<dl class="row mb-0 mt-2" style="max-width: 32rem">
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Cache" %}</dt><dd class="col-6 mb-1">{{ disk.cache }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "IO mode" %}</dt><dd class="col-6 mb-1">{{ disk.io }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Discard" %}</dt><dd class="col-6 mb-1">{{ disk.discard }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Detect zeroes" %}</dt><dd class="col-6 mb-1">{{ disk.detect_zeroes }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Serial" %}</dt><dd class="col-6 mb-1">{{ disk.serial|default:"—" }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Read-only" %}</dt><dd class="col-6 mb-1">{{ disk.readonly|yesno }}</dd>
|
||||
<dt class="col-6 fw-normal text-body-secondary">{% trans "Shareable" %}</dt><dd class="col-6 mb-0">{{ disk.shareable|yesno }}</dd>
|
||||
</dl>
|
||||
</details>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="network">
|
||||
|
|
|
|||
|
|
@ -90,13 +90,13 @@ class PendingEditsTestCase(TestCase):
|
|||
doc = self.page(nics=(nics, nics))
|
||||
self.assertEqual(doc.xpath("//div[@id='network']//div[article]/@class"), wide)
|
||||
|
||||
def test_one_disk_card_spans_the_row_and_more_sit_side_by_side(self):
|
||||
for disks, wide in (([disk("vda", "default", "")], ["col-12"]),
|
||||
([disk("vda", "default", ""), disk("vdb", "default", "")], ["col-12 col-md-6"] * 2)):
|
||||
with self.subTest(count=len(disks)):
|
||||
doc = self.page(live=disks, config=disks)
|
||||
self.assertEqual(doc.xpath("//div[@id='disks']//div[article[starts-with(@aria-labelledby, 'disk-')]]/@class"),
|
||||
wide)
|
||||
def test_the_disks_are_rows_of_one_list(self):
|
||||
disks = [disk("vda", "default", "") | {"used": 1 << 29}, disk("vdb", "default", "") | {"used": (1 << 30) + (1 << 20)}]
|
||||
doc = self.page(live=disks, config=disks)
|
||||
rows = doc.xpath("//div[@id='disks']//div[contains(@class, 'list-group')]/article[starts-with(@aria-labelledby, 'disk-')]")
|
||||
self.assertEqual([row.get("aria-labelledby") for row in rows], ["disk-vda", "disk-vdb"])
|
||||
# the used share as a bar, full when a qcow2 file outgrows its capacity
|
||||
self.assertEqual(doc.xpath("//div[@id='disks']//div[@role='progressbar']/@aria-valuenow"), ["50", "100"])
|
||||
|
||||
def test_the_clone_form_lists_the_persistent_devices(self):
|
||||
nic = {"type": "network", "mac": "52:54:10:00:00:01", "nic": "default", "state": "up", "model": "virtio",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue