mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
Twenty-five prompts asked only "Are you sure?", and on the storage page one handler asked the same before every button, Start and Enable included. Each destructive button now names its object and says what happens: a stopped network cuts running VMs off, a deleted pool keeps its files, a deleted volume loses its data, a reverted snapshot loses the changes made since. Saves no longer ask, and disabled buttons lose prompts they could not show. The VM QoS messages said to restart the network where the VM has to be powered off and on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
190 lines
14 KiB
HTML
Executable file
190 lines
14 KiB
HTML
Executable file
{% load i18n %}
|
|
{% load bootstrap_icons %}
|
|
{% load operations %}
|
|
<div class="tab-pane" id="snapshots" role="tabpanel">
|
|
{% with busy=instance.snapshot_jobs held=instance.held_by_job %}
|
|
<!-- Nav tabs -->
|
|
<ul class="nav nav-tabs" role="tablist" aria-label="Instance snapshot menu">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#takesnapshot" type="button" role="tab" aria-controls="takesnapshot" aria-selected="true">
|
|
{% trans "Take Snapshot - Internal" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#takeextsnapshot" type="button" role="tab" aria-controls="externalSnapshot" aria-selected="false">
|
|
{% trans "Take Snapshot - External" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#managesnapshot" type="button" role="tab" aria-controls="managesnapshot" aria-selected="false">
|
|
{% trans "Manage Snapshots" %}
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
<!-- Tab panes -->
|
|
<div class="tab-content">
|
|
{% if busy %}
|
|
<div class="alert alert-warning mt-3" role="status">
|
|
{% trans "A disk job is running on this VM; snapshot actions are unavailable until it ends." %}
|
|
</div>
|
|
{% endif %}
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="takesnapshot">
|
|
{% if instance.status != 5 %}
|
|
<p>{% trans "With running machine, internal snapshots may take more than an hour, depending on how much memory has on your instance and how large the disk is." %} </p>
|
|
<p class="text-danger">{% trans "The VM is paused while its memory is saved." %}</p>
|
|
{% else %}
|
|
<p>{% trans "Create an internal snapshot" %}</p>
|
|
{% endif %}
|
|
<form action="{% url 'instances:snapshot' instance.id %}" method="post" role="form" aria-label="Create snapshot form">
|
|
{% csrf_token %}{% submission_key %}
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Snapshot Name" %}" maxlength="14">
|
|
<span class="input-group-text">|</span>
|
|
<input type="text" class="form-control form-control-lg" name="description" placeholder="{% trans "Snapshot Description" %}" maxlength="45">
|
|
{% if instance.external_snapshots or busy or held %}
|
|
<button type="submit" class="btn btn-lg btn-success float-end" name="snapshot" disabled>{% trans "Take Snapshot" %}</button>
|
|
{% else %}
|
|
<button type="submit" class="btn btn-lg btn-success float-end" name="snapshot">{% trans "Take Snapshot" %}</button>
|
|
{% endif %}
|
|
</div>
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
</div> <!-- Tab pane - takesnapshot -->
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="takeextsnapshot">
|
|
<p>{% trans "Create an external snapshot. Each disk keeps its state in a new file next to it." %}</p>
|
|
<form action="{% url 'instances:create_external_snapshot' instance.id %}" method="post" role="form" aria-label="Create external snapshot form">
|
|
{% csrf_token %}{% submission_key %}
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Snapshot Name" %}" maxlength="64" pattern="[A-Za-z0-9_\-][A-Za-z0-9._\-]{0,63}" title="{% trans "Letters, digits, '.', '_' or '-'; not starting with '.'" %}" required>
|
|
<span class="input-group-text">|</span>
|
|
<input type="text" class="form-control form-control-lg" name="description" placeholder="{% trans "Snapshot Description" %}" maxlength="45">
|
|
{% if instance.internal_snapshots or busy or held %}
|
|
<button type="submit" class="btn btn-lg btn-success float-end" name="snapshot" disabled>{% trans "Take Snapshot" %}</button>
|
|
{% else %}
|
|
<button type="submit" class="btn btn-lg btn-success float-end" name="snapshot">{% trans "Take Snapshot" %}</button>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" name="memory" id="snapshot-memory" {% if instance.status != 1 %}disabled{% endif %}
|
|
onchange="var q = document.getElementById('snapshot-quiesce'); if (q.dataset.offered) { q.disabled = this.checked; q.checked = !this.checked; }">
|
|
<label class="form-check-label" for="snapshot-memory">{% trans "Save memory too (running VMs only); the VM resumes as it was when reverted" %}</label>
|
|
</div>
|
|
<div class="form-check mb-3">
|
|
{% if instance.status == 1 and instance.guest_agent_ready %}
|
|
<input class="form-check-input" type="checkbox" name="quiesce" id="snapshot-quiesce" data-offered="1" checked>
|
|
{% else %}
|
|
<input class="form-check-input" type="checkbox" name="quiesce" id="snapshot-quiesce" checked disabled>
|
|
{% endif %}
|
|
<label class="form-check-label" for="snapshot-quiesce">{% trans "Freeze the guest's file systems while the disks are snapshotted (running VMs with a connected QEMU guest agent; not with memory)" %}</label>
|
|
</div>
|
|
<p class="small">{% trans "A VM with snapshots cannot be migrated." %}</p>
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
</div> <!--tab pane takeextsnapshot-->
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
|
|
{% if instance.internal_snapshots or instance.external_snapshots %}
|
|
<p>{% trans "Choose a snapshot for restore/delete" %}</p>
|
|
{% if instance.internal_snapshots %}
|
|
<h6>{% trans "Internal" %}</h6>
|
|
<div class="table-responsive">
|
|
<table class="table" id="internal-snapshots">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "Date" %}</th>
|
|
<th scope="col">{% trans "Name" %}</th>
|
|
<th scope="col">{% trans "Description" %}</th>
|
|
<th scope="colgroup" colspan="2">{% trans "Action" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for snap in instance.internal_snapshots %}
|
|
<tr>
|
|
<td>{{ snap.date|date:"M d H:i:s" }}</td>
|
|
<td><strong>{{ snap.name }}</strong></td>
|
|
<td>{{ snap.description }}</td>
|
|
<td style="width:30px;">
|
|
<form action="{% url 'instances:revert_snapshot' instance.id %}" method="post" role="form" aria-label="Restore snapshot form">
|
|
{% csrf_token %}{% submission_key %}
|
|
<input type="hidden" name="name" value="{{ snap.name }}">
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-sm btn-primary" name="revert_snapshot" title="{% trans 'Revert to this Snapshot' %}" data-confirm="{% blocktrans with name=snap.name %}Revert to snapshot {{ name }}? Disk changes made since it are lost.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)" {% if busy or held %}disabled{% endif %}>
|
|
<span>{% bs_icon 'download' %} </span>
|
|
</button>
|
|
{% else %}
|
|
<button type="button" class="btn btn-sm btn-primary disabled"
|
|
title="{% trans "To restore snapshots you need Power Off the instance." %}">
|
|
<span>{% bs_icon 'download' %} </span>
|
|
</button>
|
|
{% endif %}
|
|
</form>
|
|
</td>
|
|
<td style="width:30px;">
|
|
<form action="{% url 'instances:delete_snapshot' instance.id %}" method="post" role="form" aria-label="Delete snapshot form">{% csrf_token %}
|
|
<input type="hidden" name="name" value="{{ snap.name }}">
|
|
<button type="submit" class="btn btn-sm btn-danger" title="{% trans 'Delete Snapshot' %}" data-confirm="{% blocktrans with name=snap.name %}Delete snapshot {{ name }}? The VM's current disks are not changed.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)" {% if busy or held %}disabled{% endif %}>
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
{% if instance.external_snapshots %}
|
|
<h6>{% trans "External" %}</h6>
|
|
<div class="table-responsive">
|
|
<table class="table" id="external-snapshots">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "Date" %}</th>
|
|
<th scope="col">{% trans "Name" %}</th>
|
|
<th scope="col">{% trans "Description" %}</th>
|
|
<th scope="colgroup" colspan="2">{% trans "Action" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for ext_snap in instance.external_snapshots %}
|
|
<tr>
|
|
<td>{{ ext_snap.date|date:"M d H:i:s" }}</td>
|
|
<td>
|
|
<strong>{{ ext_snap.name }}</strong>
|
|
{% if ext_snap.current %}<span class="badge text-bg-secondary">{% trans "current" %}</span>{% endif %}
|
|
{% if ext_snap.memory %}<span class="badge text-bg-info">{% trans "memory" %}</span>{% endif %}
|
|
</td>
|
|
<td>{{ ext_snap.description }}</td>
|
|
<td style="width:30px;">
|
|
<form action="{% url 'instances:revert_external_snapshot' instance.id %}" method="post" role="form" aria-label="Restore external snapshot form">
|
|
{% csrf_token %}{% submission_key %}
|
|
<input type="hidden" name="name" value="{{ ext_snap.name }}">
|
|
<button type="submit" class="btn btn-sm btn-primary" name="revert_external_snapshot" title="{% trans 'Revert to this Snapshot' %}"
|
|
data-confirm="{% trans 'Revert to this snapshot? Changes made since it are lost.' %}{% if not ext_snap.memory %}{% if instance.status == 1 or instance.status == 3 %} {% trans "The VM is powered off and starts again from the snapshot's disks, as after a power cut." %}{% endif %}{% endif %}{% if ext_snap.detaches %} {% trans 'These disks are detached; their writes since the current snapshot are discarded and their files are kept:' %} {% for d in ext_snap.detaches %}{{ d.dev }} ({{ d.path }}){% if not forloop.last %}, {% endif %}{% endfor %}.{% endif %}{% if instance.nvram %} {% trans 'Firmware variables (NVRAM) are not restored.' %}{% endif %}"
|
|
onclick="return confirm(this.dataset.confirm)" {% if busy or held %}disabled{% endif %}>
|
|
<span>{% bs_icon 'download' %} </span>
|
|
</button>
|
|
</form>
|
|
</td>
|
|
<td style="width:30px;">
|
|
<form action="{% url 'instances:delete_external_snapshot' instance.id %}" method="post" role="form" aria-label="Delete external snapshot form">{% csrf_token %}{% submission_key %}
|
|
<input type="hidden" name="name" value="{{ ext_snap.name }}">
|
|
<button type="submit" class="btn btn-sm btn-danger" name="delete_external_snapshot" title="{% trans 'Delete Snapshot' %}"
|
|
data-confirm="{% trans 'Delete this snapshot? Its changes are merged into the next one; nothing is lost.' %}"
|
|
onclick="return confirm(this.dataset.confirm)" {% if busy or held %}disabled{% endif %}>
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<p>{% trans "You do not have any snapshots" %}</p>
|
|
{% endif %}
|
|
</div> <!--tab pane managesnapshot-->
|
|
</div> <!-- Tab content-->
|
|
{% endwith %}
|
|
</div> <!-- Tab panel-->
|