1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/instances/test_migration_log.py
catborise 5061cc7f2c fix(instances): a migration runs in the mode the VM's state allows, and is logged as it ran
moveto read the VM state for each flag and dropped options that did
not fit it, so a request could run as something else than asked; the
log then named the mode from the form ("live" or "offline"), not the
one used. The API did not log at all and answered "instance migrate is
started" after the migration had finished.

- moveto reads the state once. A running VM migrates live, or without
  live paused for the transfer (non-live); a paused VM stays paused; a
  shut-off VM moves offline. Live for a shut-off VM, offline for a
  running or paused one, and any other state are refused before
  anything changes. unsafe, auto converge (live only) and compressed
  apply to a running VM.
- migrate_instance logs the outcome for the VM page and the API: the
  mode and options used, or the error. The API answers
  "instance is migrated (<mode>)" and accepts requests that leave out
  an optional option.
- Post copy is gone from the form, the view and the API: only the
  flag was set, and without migrateStartPostCopy, which the app never
  called, the migration never switched to post copy.
2026-10-06 15:26:49 +03:00

73 lines
3.9 KiB
Python

"""A migration is logged where it runs (migrate_instance), for the VM page
and the API alike: the mode it used on success, the error on failure."""
from unittest.mock import MagicMock, patch
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from logs.models import Logs
from rest_framework.test import APIClient
from instances.models import Instance
from instances.utils import migrate_instance
from vrtManager import util
class MigrationLogTests(TestCase):
def setUp(self):
self.admin = get_user_model().objects.create_superuser("ml-admin", "ml@example.com", "pw")
self.source = Compute.objects.create(name="ml-src", hostname="198.51.100.8", login="root", password="", type=2)
self.dest = Compute.objects.create(name="ml-dst", hostname="198.51.100.9", login="root", password="", type=2)
self.vm = Instance.objects.create(compute=self.source, name="vm", uuid="u-ml")
def migrate(self, outcome, **request):
with patch("instances.utils.connection_manager.host_is_up", return_value=True), \
patch("instances.utils.wvmInstances") as instances, \
patch("instances.utils.wvmInstance"), \
patch.object(Instance, "proxy", MagicMock()), \
patch.object(Instance, "autostart", False):
if isinstance(outcome, Exception):
instances.return_value.moveto.side_effect = outcome
else:
instances.return_value.moveto.return_value = outcome
return migrate_instance(self.dest, self.vm, self.admin, **request)
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"))
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.vm.compute = self.source
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")
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",
)
def test_the_page_and_the_api_log_once_through_migrate_instance(self):
self.client.force_login(self.admin)
api = APIClient()
api.force_authenticate(self.admin)
with patch("instances.views.utils.migrate_instance", return_value="live") as page_migrate:
self.client.post(reverse("instances:migrate", args=[self.vm.id]),
{"compute_id": self.dest.id, "live_migrate": "true"})
with patch("instances.api.viewsets.migrate_instance", return_value="live") as api_migrate:
response = api.post("/api/v1/migrate/", {"instance": self.vm.id, "target_compute": self.dest.id,
"live": True, "offline": False}, format="json")
self.assertEqual(page_migrate.call_count, 1)
self.assertEqual(api_migrate.call_count, 1)
self.assertFalse(Logs.objects.exists()) # migrate_instance logs; it is patched here
self.assertEqual(response.json(), {"status": "instance is migrated (live)"})