mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
fix(admin): Removed VMs shows the host a VM left and confirms a delete
A tombstone kept the VM's name and UUID but not its compute, so the page could not say where the VM had been. It now stores the compute's name (as text: the compute may be the one deleted) and the list shows it; records made before this have no host. Deleting a record says so, and the admin tables use scope="col" instead of the invalid span="col". Closes #217
This commit is contained in:
parent
f7279610a8
commit
6c2704f97d
9 changed files with 47 additions and 19 deletions
|
|
@ -26,8 +26,8 @@
|
|||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th span="col">{% trans "Group Name" %}</th>
|
||||
<th span="col">{% trans "Actions" %}</th>
|
||||
<th scope="col">{% trans "Group Name" %}</th>
|
||||
<th scope="col">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
|
|
|
|||
|
|
@ -24,12 +24,13 @@
|
|||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th span="col">{% trans "Name" %}</th>
|
||||
<th span="col">{% trans "UUID" %}</th>
|
||||
<th span="col">{% trans "Owners" %}</th>
|
||||
<th span="col">{% trans "Removed" %}</th>
|
||||
<th span="col">{% trans "Expires" %}</th>
|
||||
<th span="col">{% trans "Actions" %}</th>
|
||||
<th scope="col">{% trans "Name" %}</th>
|
||||
<th scope="col">{% trans "UUID" %}</th>
|
||||
<th scope="col">{% trans "Host" %}</th>
|
||||
<th scope="col">{% trans "Owners" %}</th>
|
||||
<th scope="col">{% trans "Removed" %}</th>
|
||||
<th scope="col">{% trans "Expires" %}</th>
|
||||
<th scope="col">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
|
|
@ -40,6 +41,7 @@
|
|||
{% if tombstone.is_template %}<span class="badge bg-secondary">{% trans "Template" %}</span>{% endif %}
|
||||
</td>
|
||||
<td>{{ tombstone.uuid }}</td>
|
||||
<td>{{ tombstone.host }}</td>
|
||||
<td>{{ tombstone.owner_names|join:", " }}</td>
|
||||
<td>{{ tombstone.removed|date:"Y-m-d H:i" }}</td>
|
||||
<td>{{ tombstone.expires|date:"Y-m-d H:i" }}</td>
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@
|
|||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th span="col">{% trans "Username" %}</th>
|
||||
<th span="col">{% trans "Status" %}</th>
|
||||
<th span="col">{% trans "Last Login" %}</th>
|
||||
<th span="col">{% trans "Template editor" %}</th>
|
||||
<th span="col">{% trans "Superuser" %}</th>
|
||||
<th span="col">{% trans "Can Clone" %}</th>
|
||||
<th span="col">{% trans "API tokens" %}</th>
|
||||
<th span="col">{% trans "Actions" %}</th>
|
||||
<th scope="col">{% trans "Username" %}</th>
|
||||
<th scope="col">{% trans "Status" %}</th>
|
||||
<th scope="col">{% trans "Last Login" %}</th>
|
||||
<th scope="col">{% trans "Template editor" %}</th>
|
||||
<th scope="col">{% trans "Superuser" %}</th>
|
||||
<th scope="col">{% trans "Can Clone" %}</th>
|
||||
<th scope="col">{% trans "API tokens" %}</th>
|
||||
<th scope="col">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class TombstoneTests(TestCase):
|
|||
self.tombstone = InstanceTombstone.objects.create(
|
||||
uuid=UUID,
|
||||
name="ts-vm",
|
||||
host="ts-host",
|
||||
owners=[
|
||||
{"user": self.owner.id, "is_change": True, "is_delete": False, "is_vnc": True},
|
||||
{"user": 999999, "is_change": False, "is_delete": False, "is_vnc": False},
|
||||
|
|
@ -37,6 +38,8 @@ class TombstoneTests(TestCase):
|
|||
self.assertContains(response, "ts-vm")
|
||||
self.assertContains(response, UUID)
|
||||
self.assertContains(response, "ts-owner")
|
||||
self.assertContains(response, "<td>ts-host</td>", html=True)
|
||||
self.assertContains(response, '<th scope="col">Host</th>', html=True)
|
||||
|
||||
def test_only_superusers_see_or_remove_them(self):
|
||||
self.client.force_login(self.owner)
|
||||
|
|
@ -51,6 +54,7 @@ class TombstoneTests(TestCase):
|
|||
self.assertTrue(InstanceTombstone.objects.exists())
|
||||
|
||||
def test_a_post_removes_it(self):
|
||||
response = self.client.post(reverse("admin:tombstone_delete", args=[self.tombstone.pk]))
|
||||
response = self.client.post(reverse("admin:tombstone_delete", args=[self.tombstone.pk]), follow=True)
|
||||
self.assertRedirects(response, reverse("admin:tombstone_list"))
|
||||
self.assertContains(response, "Removed VM ts-vm deleted")
|
||||
self.assertFalse(InstanceTombstone.objects.exists())
|
||||
|
|
|
|||
|
|
@ -338,6 +338,7 @@ def tombstone_delete(request, pk):
|
|||
if request.method == "POST":
|
||||
with audit(request, None, f"Removed VM {tombstone.name} ({tombstone.uuid})", "delete"):
|
||||
tombstone.delete()
|
||||
messages.success(request, _("Removed VM %(name)s deleted") % {"name": tombstone.name})
|
||||
return redirect("admin:tombstone_list")
|
||||
|
||||
return render(
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ class OwnershipTestCase(TestCase):
|
|||
def test_a_host_added_again_gets_its_vms_back_with_their_owners(self):
|
||||
self.owned_vm(self.host_a)
|
||||
self.delete(self.host_a)
|
||||
self.assertEqual(InstanceTombstone.objects.get().owners[0]["user"], self.owner.id)
|
||||
tombstone = InstanceTombstone.objects.get()
|
||||
self.assertEqual((tombstone.owners[0]["user"], tombstone.host), (self.owner.id, "host-a"))
|
||||
self.assertEqual(self.rows(), [
|
||||
("cd-admin", "-", "Login"),
|
||||
("-", "host-a", f"VM vm ({UUID}): owners kept on the Removed VMs page"),
|
||||
|
|
|
|||
|
|
@ -100,7 +100,8 @@ def _bury(inst):
|
|||
for ui in UserInstance.objects.filter(instance=inst)
|
||||
]
|
||||
if owners or inst.is_template:
|
||||
InstanceTombstone.objects.create(uuid=inst.uuid, name=inst.name, is_template=inst.is_template, owners=owners)
|
||||
InstanceTombstone.objects.create(uuid=inst.uuid, name=inst.name, host=inst.compute.name,
|
||||
is_template=inst.is_template, owners=owners)
|
||||
_log(inst, "owners kept on the Removed VMs page")
|
||||
else:
|
||||
_log(inst, "left the panel (no owners)")
|
||||
|
|
|
|||
18
instances/migrations/0014_instancetombstone_host.py
Normal file
18
instances/migrations/0014_instancetombstone_host.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.18 on 2026-10-26 08:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('instances', '0013_instancetombstone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='instancetombstone',
|
||||
name='host',
|
||||
field=models.CharField(blank=True, max_length=64),
|
||||
),
|
||||
]
|
||||
|
|
@ -398,6 +398,7 @@ class InstanceTombstone(models.Model):
|
|||
|
||||
uuid = models.CharField(max_length=36, db_index=True)
|
||||
name = models.CharField(max_length=120)
|
||||
host = models.CharField(max_length=64, blank=True) # the compute's name: the compute may be deleted
|
||||
is_template = models.BooleanField(default=False)
|
||||
owners = models.JSONField(default=list) # [{"user": id, "is_change": .., "is_delete": .., "is_vnc": ..}]
|
||||
removed = models.DateTimeField(auto_now_add=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue