1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00

fix(instances): a paused VM can be destroyed from its page

The Destroy button was disabled and the confirmation page refused a
paused VM ("Instance is suspended, cannot destroy!"), while the destroy
request itself stops a paused VM like a running one. Both now offer it.
This commit is contained in:
catborise 2026-10-08 08:32:10 +03:00
parent 55eb63a09f
commit be00b1cf8e
4 changed files with 30 additions and 29 deletions

View file

@ -10,32 +10,26 @@
{% block content %}
{% if can_delete %}
{% if instance.status == 3 %}
<div class="alert alert-danger">
{% trans "Instance is suspended, cannot destroy!" %}
<div class="alert alert-danger">
{% trans "This action is irreversible!" %}
</div>
<form action="{% url 'instances:destroy' instance.id %}" class="form" method="post" role="form" id="delete_form">{% csrf_token %}
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="delete_disk" value="true" checked id="delete_disk">
<label class="form-check-label fw-bold" for="delete_disk">{% trans "Remove Instance's data" %}</label>
</div>
{% else %}
<div class="alert alert-danger">
{% trans "This action is irreversible!" %}
{% if instance.nvram %}
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="delete_nvram" value="true" id="delete_nvram" checked>
<label class="form-check-label fw-bold" for="delete_nvram">
{% trans "Remove Instance's NVRAM" %}
</label>
</div>
<form action="{% url 'instances:destroy' instance.id %}" class="form" method="post" role="form" id="delete_form">{% csrf_token %}
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="delete_disk" value="true" checked id="delete_disk">
<label class="form-check-label fw-bold" for="delete_disk">{% trans "Remove Instance's data" %}</label>
</div>
{% if instance.nvram %}
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="delete_nvram" value="true" id="delete_nvram" checked>
<label class="form-check-label fw-bold" for="delete_nvram">
{% trans "Remove Instance's NVRAM" %}
</label>
</div>
{% endif %}
<button type="submit" class="btn btn-lg btn-success float-end" name="delete">
{% trans "Destroy" %}
</button>
</form>
{% endif %}
{% endif %}
<button type="submit" class="btn btn-lg btn-success float-end" name="delete">
{% trans "Destroy" %}
</button>
</form>
{% else %}
<div class="alert alert-danger" role="alert">
{% trans "You cannot destroy instance!" %}

View file

@ -13,11 +13,7 @@
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
<p>{% trans 'This action starts remove instance process' %}</p>
{% if can_delete %}
{% if instance.status == 3 %}
<a class="btn btn-lg btn-success disabled float-end">{% trans "Destroy" %}</a>
{% else %}
<a href="{% url 'instances:destroy' instance.id %}" class="btn btn-lg btn-success float-end">{% trans "Destroy" %}</a>
{% endif %}
<a href="{% url 'instances:destroy' instance.id %}" class="btn btn-lg btn-success float-end">{% trans "Destroy" %}</a>
{% else %}
<button class="btn btn-lg btn-success disabled float-end" name="delete">{% trans "Destroy" %}</button>
{% endif %}

View file

@ -65,3 +65,9 @@ class DestroyOrderTestCase(TestCase):
response = self.client.get(reverse("instances:destroy", args=[self.instance.id]))
self.assertEqual(response.status_code, 200)
self.assertEqual(calls, [])
def test_the_confirmation_page_of_a_paused_vm_offers_the_form(self):
with patch("instances.models.wvmInstance") as wvm:
wvm.return_value.get_status.return_value = 3 # paused
response = self.client.get(reverse("instances:destroy", args=[self.instance.id]))
self.assertIn('id="delete_form"', response.content.decode())

View file

@ -145,6 +145,11 @@ class PageControlsTestCase(PageControls, TestCase):
owner={"is_change": True})
self.assertEqual(self.controls(changer), CONSOLE | POWER | CHANGE | {"clone", "snapshots"})
def test_a_paused_vm_can_be_destroyed(self):
# destroy stops a paused VM like a running one
owner = self.user("paused_deleter", owner={"is_delete": True})
self.assertIn("destroy", self.controls(owner, status=3))
def test_owner_with_change_and_no_extra_permissions(self):
changer = self.user("ctl_plain_changer", owner={"is_change": True})
self.assertEqual(self.controls(changer), CONSOLE | POWER | CHANGE)