Skip to content

Commit

Permalink
api/admin: implement *.property.GetAll methods
Browse files Browse the repository at this point in the history
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
  • Loading branch information
marmarek committed Dec 5, 2019
1 parent 5d77cf2 commit 10f99e5
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down
42 changes: 38 additions & 4 deletions qubes/api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,29 +169,63 @@ def _property_get(self, dest):

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'
elif property_def.type is int:
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
Expand Down
32 changes: 32 additions & 0 deletions qubes/tests/api_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,38 @@ def test_026_vm_property_get_default_bool(self):
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)
Expand Down

0 comments on commit 10f99e5

Please sign in to comment.