1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00
webvirtcloud/instances/tests/test_migration_bookkeeping.py
catborise c1d36053d6 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>
2026-10-24 14:14:53 +03:00

70 lines
3.5 KiB
Python

"""What migrate_instance does around the migration itself: it records a
finished migration even when autostart fails."""
from unittest.mock import MagicMock, patch
from accounts.models import UserInstance
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.db import DatabaseError
from django.test import TestCase
from logs.models import Logs
from instances.models import Instance
from instances.utils import migrate_instance
from vrtManager import util
class MigrationBookkeepingTests(TestCase):
def setUp(self):
self.admin = get_user_model().objects.create_superuser("mb-admin", "mb@example.com", "pw")
self.source = Compute.objects.create(name="mb-src", hostname="198.51.100.8", login="root", password="", type=2)
self.dest = Compute.objects.create(name="mb-dst", hostname="198.51.100.9", login="root", password="", type=2)
self.vm = Instance.objects.create(compute=self.source, name="vm", uuid="u-mb", is_template=True)
def migrate(self, vm=None, up=True, autostart=False, autostart_error=None):
proxy = MagicMock()
proxy.get_autostart.return_value = autostart
with patch.object(Compute, "status", up), \
patch("instances.utils.wvmInstances") as instances, \
patch("instances.utils.wvmInstance") as instance, \
patch.object(Instance, "proxy", proxy):
instances.return_value.moveto.return_value = "live"
if autostart_error:
instance.return_value.set_autostart.side_effect = autostart_error
self.moveto = instances.return_value.moveto
self.set_autostart = instance.return_value.set_autostart
return migrate_instance(self.dest, vm or self.vm, self.admin, live=True)
def test_autostart_is_read_from_libvirt_and_restored_on_the_destination(self):
self.vm.__dict__["autostart"] = False # an earlier, cached read
self.migrate(autostart=True)
self.set_autostart.assert_called_once_with(1)
def test_a_failed_autostart_does_not_stop_the_records(self):
self.migrate(autostart=True, autostart_error=util.OperationError("no autostart"))
self.assertEqual(Instance.objects.get(id=self.vm.id).compute, self.dest)
self.assertEqual(list(Logs.objects.order_by("id").values_list("message", "success")), [
("VM vm: migrate (live) to 198.51.100.9", True),
("VM vm: restore autostart on 198.51.100.9: no autostart", False),
])
def test_a_failed_record_is_logged_as_such(self):
with patch.object(Instance, "save", side_effect=DatabaseError("locked")), \
self.assertRaises(DatabaseError):
self.migrate()
self.assertEqual(list(Logs.objects.order_by("id").values_list("message", "success")), [
("VM vm: migrate (live) to 198.51.100.9", True),
("VM vm: record the move to 198.51.100.9: DatabaseError", False),
])
def test_a_record_the_destination_already_has_keeps_template_and_owners(self):
owner = get_user_model().objects.create_user("mb-owner")
UserInstance.objects.create(user=owner, instance=self.vm, is_change=True)
found = Instance.objects.create(compute=self.dest, name="vm", uuid="u-mb") # listed there meanwhile
old_id = self.vm.id
self.migrate()
self.assertFalse(Instance.objects.filter(id=old_id).exists())
found.refresh_from_db()
self.assertTrue(found.is_template)
self.assertEqual(UserInstance.objects.get(user=owner).instance_id, found.id)