1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00

fix(instances): every end of a migration job leaves a Logs row

Only migrate_instance, inside the job's run, wrote the migration's row:
a migration refused by precheck, cancelled while queued, or ended by
recovery after a worker restart left none. The job now has a log entry
for the ends migrate_instance did not log; run marks those it did.
This commit is contained in:
catborise 2026-11-05 16:00:32 +03:00
parent 62976cbd55
commit 1089b914ed
2 changed files with 26 additions and 1 deletions

View file

@ -968,7 +968,6 @@ class MigrateInstance(Kind):
ctx.record_intent()
ctx.set_phase(gettext_noop("Migrating the VM"))
try:
# migrate_instance logs the outcome
utils.migrate_instance(Compute.objects.get(pk=p["target"]), instance, op.user,
disk_pools=p["disk_pools"],
**{k: p[k] for k in MIGRATE_OPTIONS})
@ -982,8 +981,19 @@ class MigrateInstance(Kind):
if isinstance(err, util.OperationError):
raise
raise util.OperationError(str(err)) from err
finally:
# migrate_instance wrote the outcome's Logs row (log_entry)
ctx.record_intent(outcome_logged=True)
return {}
def log_entry(self, op, state):
"""A row for an end migrate_instance did not log: refused before it
ran, cancelled while queued, or ended by recovery."""
if op.intent.get("outcome_logged"):
return None
target = Compute.objects.filter(pk=op.params["target"]).values_list("name", flat=True).first()
return _log(op, state, f"migrate to {target}")
def progress(self, op):
info = _proxy(_instance(op.params["instance"])).instance.jobInfo()
return (info[4], info[3]) if info[3] else None

View file

@ -213,3 +213,18 @@ class MigrateJobTestCase(TransactionTestCase):
proxy.return_value.instance.isActive.return_value = False
get_kind(op.kind).cancel(op)
proxy.return_value.instance.abortJob.assert_not_called()
def test_each_end_of_a_migration_leaves_one_logs_row(self):
from logs.models import Logs
op = self.queue(live_migrate="true")
ctx = self.start(op)
get_kind(op.kind).run(op, ctx) # migrate_instance writes its row
op.refresh_from_db()
self.assertIsNone(MigrateInstance().log_entry(op, Operation.SUCCEEDED))
self.assertEqual(Logs.objects.filter(instance_uuid=UUID).count(), 1)
# ended without migrate_instance: refused, cancelled while queued, or by recovery
queued = Operation(params={"instance": self.vm.pk, "target": self.dest.pk}, intent={"uuid": UUID},
resource_label="mig-src/mig-vm")
self.assertEqual(MigrateInstance().log_entry(queued, Operation.CANCELLED),
("mig-src", "mig-vm", UUID, "VM mig-vm: migrate to mig-dst (cancelled)"))