mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
feat(logs): every VM operation writes an audit row the VM's Logs tab finds by UUID
Operations on a VM now write their rows the way the admin audit does: one
row per operation, done or failed with a safe reason, with the user, IP and
host. Each row names the VM ("VM web-1: delete snapshot snap1") and carries
its UUID in the new Logs.instance_uuid column.
- audit_vm and failed_vm wrap audit() and failed() for a VM. A failure the
view turns into a message is caught outside the block, so its failed row
is written; busy claims, permission and quota refusals and invalid input
write nothing. No password, key or XML goes into a row.
- All VM page operations move from addlogmsg to audit_vm, and addlogmsg
goes. QoS changes and defining a VM from XML are logged now; a gstfsd
error reply (root password, SSH key) is a failed row, not a done one.
- Partial results are rows of their own: the VM delete, then each disk
delete; the disk detach, then the volume delete; the migration, then a
failed autostart restore, NVRAM removal or record on the target.
- Migration rows use the compute name as the host column, as every other
row does.
- VM job rows (create, clone, snapshots) and their resolutions carry the
VM's UUID; snapshot job intents now record it. The owner rows of the VM
page and the accounts pages, and the ownership system rows, carry it too.
- The Logs tab loads vm_logs/<pk>/ and shows the rows with the VM's UUID,
so a rename keeps the history, two VMs with one name are told apart and
a VM on two computes is one VM. It gains a Result column. Rows from
before this change have no UUID and stay on the Logs page only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
49cf16ecbe
commit
c1d36053d6
24 changed files with 639 additions and 524 deletions
|
|
@ -1,5 +1,6 @@
|
|||
"""A migration is logged where it runs (migrate_instance, in the job): the
|
||||
mode it used on success, the error on failure."""
|
||||
mode it used on success, the error on failure, and each later step that
|
||||
failed in a row of its own."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
|
@ -14,6 +15,10 @@ from instances.utils import migrate_instance
|
|||
from vrtManager import util
|
||||
|
||||
|
||||
def rows():
|
||||
return list(Logs.objects.order_by("id").values_list("message", "success"))
|
||||
|
||||
|
||||
class MigrationLogTests(TestCase):
|
||||
def setUp(self):
|
||||
self.admin = get_user_model().objects.create_superuser("ml-admin", "ml@example.com", "pw")
|
||||
|
|
@ -45,7 +50,7 @@ class MigrationLogTests(TestCase):
|
|||
path, destination = self.proxy.remove_nvram.call_args[0]
|
||||
self.assertEqual(path, "/var/lib/libvirt/qemu/nvram/vm_VARS.fd")
|
||||
self.assertIsNotNone(destination)
|
||||
self.assertEqual(Logs.objects.get().message, "Instance is migrated(live) to 198.51.100.9")
|
||||
self.assertEqual(rows(), [("VM vm: migrate (live) to 198.51.100.9", True)])
|
||||
|
||||
def test_a_vm_without_nvram_removes_nothing(self):
|
||||
self.migrate("live", live=True)
|
||||
|
|
@ -54,11 +59,10 @@ class MigrationLogTests(TestCase):
|
|||
def test_a_failed_nvram_removal_is_logged_but_the_migration_stands(self):
|
||||
self.migrate("live", nvram="/var/lib/libvirt/qemu/nvram/vm_VARS.fd",
|
||||
remove_nvram=libvirtError("cannot delete"), live=True)
|
||||
self.assertEqual(
|
||||
Logs.objects.get().message,
|
||||
"Instance is migrated(live) to 198.51.100.9; removing its NVRAM file "
|
||||
"/var/lib/libvirt/qemu/nvram/vm_VARS.fd on the source failed: cannot delete",
|
||||
)
|
||||
self.assertEqual(rows(), [
|
||||
("VM vm: migrate (live) to 198.51.100.9", True),
|
||||
("VM vm: remove NVRAM file /var/lib/libvirt/qemu/nvram/vm_VARS.fd on the source: cannot delete", False),
|
||||
])
|
||||
self.vm.refresh_from_db()
|
||||
self.assertEqual(self.vm.compute, self.dest)
|
||||
|
||||
|
|
@ -67,7 +71,9 @@ class MigrationLogTests(TestCase):
|
|||
remove_nvram=AttributeError("unexpected"), live=True)
|
||||
self.vm.refresh_from_db()
|
||||
self.assertEqual(self.vm.compute, self.dest)
|
||||
self.assertIn("on the source failed: unexpected", Logs.objects.get().message)
|
||||
# an unknown error gives only its class name
|
||||
self.assertEqual(rows()[-1], ("VM vm: remove NVRAM file /var/lib/libvirt/qemu/nvram/vm_VARS.fd on the source: "
|
||||
"AttributeError", False))
|
||||
|
||||
def test_a_failed_migration_removes_nothing(self):
|
||||
with self.assertRaises(util.OperationError):
|
||||
|
|
@ -77,23 +83,20 @@ class MigrationLogTests(TestCase):
|
|||
def test_success_names_the_mode_used(self):
|
||||
self.migrate("non-live, compressed", live=False, compress=True)
|
||||
log = Logs.objects.get()
|
||||
self.assertEqual(log.message, "Instance is migrated(non-live, compressed) to 198.51.100.9")
|
||||
self.assertEqual((log.user, log.host, log.instance), ("ml-admin", "198.51.100.8", "vm"))
|
||||
self.assertEqual(log.message, "VM vm: migrate (non-live, compressed) to 198.51.100.9")
|
||||
self.assertEqual((log.user, log.host, log.instance, log.instance_uuid), ("ml-admin", "ml-src", "vm", "u-ml"))
|
||||
|
||||
def test_success_names_the_migration_address(self):
|
||||
self.dest.migration_address = "203.0.113.9"
|
||||
self.migrate("live", live=True)
|
||||
self.assertEqual(Logs.objects.get().message, "Instance is migrated(live) to 198.51.100.9 via 203.0.113.9")
|
||||
self.assertEqual(Logs.objects.get().message, "VM vm: migrate (live) to 198.51.100.9 via 203.0.113.9")
|
||||
self.vm.compute = self.source
|
||||
self.vm.save()
|
||||
self.dest.migration_address = "198.51.100.9" # the host name itself
|
||||
self.migrate("live", live=True)
|
||||
self.assertEqual(Logs.objects.latest("id").message, "Instance is migrated(live) to 198.51.100.9")
|
||||
self.assertEqual(Logs.objects.latest("id").message, "VM vm: migrate (live) to 198.51.100.9")
|
||||
|
||||
def test_failure_is_logged_and_raised(self):
|
||||
with self.assertRaisesRegex(util.OperationError, "A shut-off VM cannot be migrated live"):
|
||||
self.migrate(util.OperationError("A shut-off VM cannot be migrated live"), live=True)
|
||||
self.assertEqual(
|
||||
Logs.objects.get().message,
|
||||
"Instance migration to 198.51.100.9 failed: A shut-off VM cannot be migrated live",
|
||||
)
|
||||
self.assertEqual(rows(), [("VM vm: migrate to 198.51.100.9: A shut-off VM cannot be migrated live", False)])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue