api/admin: implement *.property.GetAll methods

Allow getting all the VM properties with one call. This greatly improve
performance of an applications retrieving many/all of them (qvm-ls,
qubes manager etc)

QubesOS/qubes-issues#5415
Fixes QubesOS/qubes-issues#3293
This commit is contained in:
Marek Marczykowski-Górecki 2019-12-03 06:27:37 +01:00
parent 5d77cf2298
commit 10f99e5c4a
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 72 additions and 4 deletions

View File

@ -35,6 +35,7 @@ ADMIN_API_METHODS_SIMPLE = \
admin.pool.volume.Set.rw \
admin.pool.volume.Snapshot \
admin.property.Get \
admin.property.GetAll \
admin.property.GetDefault \
admin.property.Help \
admin.property.HelpRst \
@ -87,6 +88,7 @@ ADMIN_API_METHODS_SIMPLE = \
admin.vm.firewall.SetPolicy \
admin.vm.firewall.Reload \
admin.vm.property.Get \
admin.vm.property.GetAll \
admin.vm.property.GetDefault \
admin.vm.property.Help \
admin.vm.property.HelpRst \

View File

@ -169,7 +169,11 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.fire_event_for_permission()
property_def = dest.property_get_def(self.arg)
return self._serialize_property(dest, self.arg)
@staticmethod
def _serialize_property(dest, prop):
property_def = dest.property_get_def(prop)
# explicit list to be sure that it matches protocol spec
if isinstance(property_def, qubes.vm.VMProperty):
property_type = 'vm'
@ -177,21 +181,51 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
property_type = 'int'
elif property_def.type is bool:
property_type = 'bool'
elif self.arg == 'label':
elif prop == 'label':
property_type = 'label'
else:
property_type = 'str'
try:
value = getattr(dest, self.arg)
value = getattr(dest, str(prop))
except AttributeError:
return 'default=True type={} '.format(property_type)
else:
return 'default={} type={} {}'.format(
str(dest.property_is_default(self.arg)),
str(dest.property_is_default(prop)),
property_type,
str(value) if value is not None else '')
@qubes.api.method('admin.vm.property.GetAll', no_payload=True,
scope='local', read=True)
@asyncio.coroutine
def vm_property_get_all(self):
"""Get values of all VM properties"""
return self._property_get_all(self.dest)
@qubes.api.method('admin.property.GetAll', no_payload=True,
scope='global', read=True)
@asyncio.coroutine
def property_get_all(self):
"""Get value all global properties"""
self.enforce(self.dest.name == 'dom0')
return self._property_get_all(self.app)
def _property_get_all(self, dest):
self.enforce(not self.arg)
properties = dest.property_list()
properties = self.fire_event_for_filter(properties)
return ''.join(
'{} {}\n'.format(str(prop),
self._serialize_property(dest, prop).
replace('\\', '\\\\').replace('\n', '\\n'))
for prop in sorted(properties))
@qubes.api.method('admin.vm.property.GetDefault', no_payload=True,
scope='local', read=True)
@asyncio.coroutine

View File

@ -180,6 +180,38 @@ class TC_00_VMs(AdminAPITestCase):
b'provides_network')
self.assertEqual(value, 'type=bool False')
def test_027_vm_property_get_all(self):
# any string property, test \n encoding
self.vm.kernelopts = 'opt1\nopt2\nopt3\\opt4'
with unittest.mock.patch.object(self.vm, 'property_list') as list_mock:
list_mock.return_value = [
self.vm.property_get_def('name'),
self.vm.property_get_def('default_user'),
self.vm.property_get_def('netvm'),
self.vm.property_get_def('klass'),
self.vm.property_get_def('debug'),
self.vm.property_get_def('label'),
self.vm.property_get_def('kernelopts'),
self.vm.property_get_def('qrexec_timeout'),
self.vm.property_get_def('qid'),
self.vm.property_get_def('updateable'),
]
value = self.call_mgmt_func(b'admin.vm.property.GetAll', b'test-vm1')
self.maxDiff = None
expected = '''debug default=True type=bool False
default_user default=True type=str user
klass default=True type=str AppVM
label default=False type=label red
name default=False type=str test-vm1
qid default=False type=int 2
qrexec_timeout default=True type=int 60
updateable default=True type=bool False
kernelopts default=False type=str opt1\\nopt2\\nopt3\\\\opt4
netvm default=True type=vm
'''
self.assertEqual(value, expected)
def test_030_vm_property_set_vm(self):
netvm = self.app.add_new_vm('AppVM', label='red', name='test-net',
template='test-template', provides_network=True)