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 c55f52f025 test: each app keeps its tests in a tests package
131 test modules sat next to the code they test; instances alone had
51 of them beside views.py and utils.py. They move into a
tests/ package per app, and the JavaScript checks into
webvirtcloud/tests/js. Each non-empty tests.py becomes tests/test_views.py;
the empty generated stubs are removed.

Imports of the app's own modules become absolute, paths built from
__file__ go one level up, the live helper is instances.tests.livetest,
and coverage omits */tests/*. The suite discovers the same 960 tests as
before.
2026-10-12 15:17:30 +03:00

97 lines
4.8 KiB
Python

"""What migrate_instance does around the migration itself: it checks the
destination with the app's connection, re-reads the VM under the compute
locks, and records a finished migration even when autostart fails."""
import re
from pathlib import Path
from unittest.mock import MagicMock, patch
from accounts.models import UserInstance
from computes.models import Compute
from django.conf import settings
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 MIGRATION_TIME_LIMIT, 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_a_destination_the_app_cannot_connect_to_is_refused(self):
with self.assertRaisesRegex(util.OperationError, "mb-dst is not reachable"):
self.migrate(up=False)
self.moveto.assert_not_called()
def test_a_vm_moved_meanwhile_is_not_migrated_again(self):
stale = Instance.objects.get(id=self.vm.id)
Instance.objects.filter(id=self.vm.id).update(compute=self.dest) # another request moved it
with self.assertRaisesRegex(util.OperationError, "no longer on mb-src"):
self.migrate(vm=stale)
self.moveto.assert_not_called()
Instance.objects.filter(id=self.vm.id).delete() # merged into a record on the destination
with self.assertRaisesRegex(util.OperationError, "no longer on mb-src"):
self.migrate(vm=stale)
self.moveto.assert_not_called()
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(
Logs.objects.get().message,
"Instance is migrated(live) to 198.51.100.9; setting its autostart there failed: no autostart",
)
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(
Logs.objects.get().message,
"Instance is migrated(live) to 198.51.100.9, but recording it failed: locked",
)
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)
def test_a_migration_is_cancelled_before_the_web_server_ends_the_request(self):
self.migrate()
self.assertEqual(self.moveto.call_args.kwargs["timeout"], MIGRATION_TIME_LIMIT)
gunicorn = (Path(settings.BASE_DIR) / "gunicorn.conf.py").read_text()
request_timeout = int(re.search(r"^timeout = (\d+)", gunicorn, re.M).group(1))
self.assertLess(MIGRATION_TIME_LIMIT, request_timeout - 30) # time to answer and log