From 84edd428c3b45fde9c4cd393fdecb8615c3f221b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 1 Mar 2026 05:06:34 +0100 Subject: [PATCH] Use admin.vm.List method to get VM class Normally, VM class is cached when getting list of all (accessible) VMs. This means the admin.vm.property.Get+klass is normally not used. But if there is a case where VM object is created without listing all VMs first, it will not get the 'klass' property set. Apparently preloaded disposables trigger this case in audiovm. The thing is, admin.vm.property.Get+klass is mostly redundant with admin.vm.List (directed at a specific qube), since admin.vm.List contains that information already - so it doesn't make much sense to force everybody to add both to the policy. Fix this by using admin.vm.List to get the VM class. Fixes QubesOS/qubes-issues#10717 --- qubesadmin/tests/vm/properties.py | 8 ++++++++ qubesadmin/vm/__init__.py | 17 +++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/qubesadmin/tests/vm/properties.py b/qubesadmin/tests/vm/properties.py index 126f2d6d..261aa936 100644 --- a/qubesadmin/tests/vm/properties.py +++ b/qubesadmin/tests/vm/properties.py @@ -20,6 +20,7 @@ # pylint: disable=missing-docstring +import qubesadmin.vm import qubesadmin.tests.vm @@ -305,3 +306,10 @@ def test_015_mem(self): ('test-vm', 'admin.vm.CurrentState', None, None)] = \ b'0\x00mem=1234' self.assertEqual(self.vm.get_mem(), 1234) + + def test_020_klass(self): + vm = qubesadmin.vm.QubesVM(self.app, "test-vm") + self.app.expected_calls[("test-vm", "admin.vm.List", None, None)] = \ + b"0\x00test-vm class=AppVM state=Running\n" + self.assertEqual(vm.klass, "AppVM") + self.assertAllCalled() diff --git a/qubesadmin/vm/__init__.py b/qubesadmin/vm/__init__.py index 1442d349..00785db8 100644 --- a/qubesadmin/vm/__init__.py +++ b/qubesadmin/vm/__init__.py @@ -446,8 +446,21 @@ def klass(self): """Qube class""" # use cached value if available if self._klass is None: - # pylint: disable=no-member - self._klass = super().klass + try: + # use List method as that should be allowed for VMs that are + # visible + vm_list_data = self.qubesd_call( + self._method_dest, "admin.vm.List" + ) + assert vm_list_data.count(b"\n") == 1 + vm_name, props = vm_list_data.decode("ascii").split(" ", 1) + assert vm_name == self.name + props = props.split(" ") + props_dict = dict([vm_prop.split("=", 1) for vm_prop in props]) + self._klass = props_dict["class"] + except qubesadmin.exc.QubesDaemonAccessError: + # pylint: disable=no-member + self._klass = super().klass return self._klass def get_notes(self) -> str: