diff --git a/instances/tests/test_vm_audit_rows.py b/instances/tests/test_vm_audit_rows.py index d417dffc..6ed180eb 100644 --- a/instances/tests/test_vm_audit_rows.py +++ b/instances/tests/test_vm_audit_rows.py @@ -90,6 +90,17 @@ class VMRowTests(TestCase): self.assertEqual(rows, [("VM vm: set console password: The VM has no graphics device", False), ("VM vm: set console password", True)]) + def test_a_console_setting_that_changed_nothing_writes_no_row(self): + self.proxy.get_console_type.return_value = "vnc" + self.proxy.set_console_keymap.return_value = False # no graphics device + self.proxy.set_console_listener_addr.return_value = False # a socket, no address + self.post("update_console", {"listen_on": "0.0.0.0", "keymap": "de"}) + self.proxy.set_console_keymap.return_value = None # the define's result + self.proxy.set_console_listener_addr.return_value = None + self.post("update_console", {"listen_on": "0.0.0.0", "keymap": "de"}) + self.assertEqual(self.rows(), [("VM vm: set console keymap de", True), + ("VM vm: set console listen address 0.0.0.0", True)]) + def test_a_deleted_vm_and_each_of_its_disks_have_their_own_row(self): self.proxy.split_disk_paths_by_use.return_value = (["/pool/a.qcow2", "/pool/b.qcow2"], []) volume = self.proxy.get_volume_by_path.return_value @@ -159,3 +170,18 @@ class JobRowTests(TestCase): op = Operation(kind=kind, resource_label=label, params=params, intent=intent) state = Operation.FAILED if kind == "snapshot.delete" else Operation.SUCCEEDED self.assertEqual(get_kind(kind).log_entry(op, state), entry) + + +class MigrationResolutionRowTests(TestCase): + def test_a_resolved_migration_carries_the_vm_uuid(self): + from operations import jobs + from operations.models import Operation + + admin = get_user_model().objects.create_superuser("res-admin", "res@example.com", "pw") + op = Operation.objects.create(kind="instance.migrate", user=admin, username=admin.username, + submission_key="key-res-00000001", resource="instance:" + UUID, + resource_label="c1/vm", state=Operation.UNCERTAIN, + intent={"compute": 1, "uuid": UUID}) + jobs.resolve(op.pk, admin, Operation.SUCCEEDED, "the VM runs on the target") + row = Logs.objects.get(message__contains="resolved") + self.assertEqual((row.instance, row.instance_uuid, row.success), ("c1/vm", UUID, True)) diff --git a/instances/views.py b/instances/views.py index 0002679f..46643774 100755 --- a/instances/views.py +++ b/instances/views.py @@ -1781,12 +1781,13 @@ def update_console(request, pk): if "keymap" in form.changed_data or "clear_keymap" in form.changed_data: keymap = "" if form.cleaned_data["clear_keymap"] else form.cleaned_data["keymap"] - with audit_vm(request, instance, f"set console keymap {keymap}" if keymap else "remove console keymap"): - instance.proxy.set_console_keymap(keymap) + # False: no graphics device, nothing changed + with audit_vm(request, instance, f"set console keymap {keymap}" if keymap else "remove console keymap") as entry: + entry.skip = instance.proxy.set_console_keymap(keymap) is False if "listen_on" in form.changed_data: - with audit_vm(request, instance, f"set console listen address {form.cleaned_data['listen_on']}"): - instance.proxy.set_console_listener_addr(form.cleaned_data["listen_on"]) + with audit_vm(request, instance, f"set console listen address {form.cleaned_data['listen_on']}") as entry: + entry.skip = instance.proxy.set_console_listener_addr(form.cleaned_data["listen_on"]) is False if not form.is_valid(): messages.error(request, _("Console settings were not saved: %(errors)s") % {"errors": form.errors.as_text()}) diff --git a/operations/jobs.py b/operations/jobs.py index 4b6adfc6..69c30aa6 100644 --- a/operations/jobs.py +++ b/operations/jobs.py @@ -262,9 +262,10 @@ def resolve(op_id, user, outcome_state, note): Operation.objects.filter(pk=op.pk).update(state=outcome_state, resolution=outcome_state, resolution_note=note, resolved_by=user, resolved_at=timezone.now()) Claim.objects.filter(operation=op).delete() - # the host, VM and UUID columns of the kind's own entries, so the VM's log shows it + # the host, VM and UUID columns of the kind's own entries, so the VM's log shows it; + # a kind without entries (a migration) still names its VM by the UUID in its intent entry = _entry(op, outcome_state) - host, instance, uuid = entry[:3] if entry is not None else ("-", op.resource_label, "") + host, instance, uuid = entry[:3] if entry is not None else ("-", op.resource_label, op.intent.get("uuid", "")) _log(user.username, host, instance, uuid, f"Job {op.pk} resolved as {outcome_state}: {note}", outcome_state == Operation.SUCCEEDED) op.refresh_from_db()