qubes/api/admin: annotate API methods

Second attempt: this time use full words for scope, read, write,
execute.

QubesOS/qubes-issues#2871
This commit is contained in:
Marek Marczykowski-Górecki 2017-06-27 01:41:52 +02:00
parent 3e0d01cfb9
commit d0663940a7
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

View File

@ -75,7 +75,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
SOCKNAME = '/var/run/qubesd.sock' SOCKNAME = '/var/run/qubesd.sock'
@qubes.api.method('admin.vmclass.List', no_payload=True) @qubes.api.method('admin.vmclass.List', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def vmclass_list(self): def vmclass_list(self):
'''List all VM classes''' '''List all VM classes'''
@ -88,7 +89,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}\n'.format(ep.name) return ''.join('{}\n'.format(ep.name)
for ep in entrypoints) for ep in entrypoints)
@qubes.api.method('admin.vm.List', no_payload=True) @qubes.api.method('admin.vm.List', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_list(self): def vm_list(self):
'''List all the domains''' '''List all the domains'''
@ -105,13 +107,15 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
vm.get_power_state()) vm.get_power_state())
for vm in sorted(domains)) for vm in sorted(domains))
@qubes.api.method('admin.vm.property.List', no_payload=True) @qubes.api.method('admin.vm.property.List', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_property_list(self): def vm_property_list(self):
'''List all properties on a qube''' '''List all properties on a qube'''
return self._property_list(self.dest) return self._property_list(self.dest)
@qubes.api.method('admin.property.List', no_payload=True) @qubes.api.method('admin.property.List', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def property_list(self): def property_list(self):
'''List all global properties''' '''List all global properties'''
@ -125,13 +129,15 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}\n'.format(prop.__name__) for prop in properties) return ''.join('{}\n'.format(prop.__name__) for prop in properties)
@qubes.api.method('admin.vm.property.Get', no_payload=True) @qubes.api.method('admin.vm.property.Get', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_property_get(self): def vm_property_get(self):
'''Get a value of one property''' '''Get a value of one property'''
return self._property_get(self.dest) return self._property_get(self.dest)
@qubes.api.method('admin.property.Get', no_payload=True) @qubes.api.method('admin.property.Get', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def property_get(self): def property_get(self):
'''Get a value of one global property''' '''Get a value of one global property'''
@ -167,14 +173,16 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
property_type, property_type,
str(value) if value is not None else '') str(value) if value is not None else '')
@qubes.api.method('admin.vm.property.Set') @qubes.api.method('admin.vm.property.Set',
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_property_set(self, untrusted_payload): def vm_property_set(self, untrusted_payload):
'''Set property value''' '''Set property value'''
return self._property_set(self.dest, return self._property_set(self.dest,
untrusted_payload=untrusted_payload) untrusted_payload=untrusted_payload)
@qubes.api.method('admin.property.Set') @qubes.api.method('admin.property.Set',
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def property_set(self, untrusted_payload): def property_set(self, untrusted_payload):
'''Set property value''' '''Set property value'''
@ -194,13 +202,15 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
setattr(dest, self.arg, newvalue) setattr(dest, self.arg, newvalue)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.property.Help', no_payload=True) @qubes.api.method('admin.vm.property.Help', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_property_help(self): def vm_property_help(self):
'''Get help for one property''' '''Get help for one property'''
return self._property_help(self.dest) return self._property_help(self.dest)
@qubes.api.method('admin.property.Help', no_payload=True) @qubes.api.method('admin.property.Help', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def property_help(self): def property_help(self):
'''Get help for one property''' '''Get help for one property'''
@ -220,13 +230,15 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return qubes.utils.format_doc(doc) return qubes.utils.format_doc(doc)
@qubes.api.method('admin.vm.property.Reset', no_payload=True) @qubes.api.method('admin.vm.property.Reset', no_payload=True,
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_property_reset(self): def vm_property_reset(self):
'''Reset a property to a default value''' '''Reset a property to a default value'''
return self._property_reset(self.dest) return self._property_reset(self.dest)
@qubes.api.method('admin.property.Reset', no_payload=True) @qubes.api.method('admin.property.Reset', no_payload=True,
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def property_reset(self): def property_reset(self):
'''Reset a property to a default value''' '''Reset a property to a default value'''
@ -242,7 +254,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
delattr(dest, self.arg) delattr(dest, self.arg)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.volume.List', no_payload=True) @qubes.api.method('admin.vm.volume.List', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_list(self): def vm_volume_list(self):
assert not self.arg assert not self.arg
@ -250,7 +263,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
volume_names = self.fire_event_for_filter(self.dest.volumes.keys()) volume_names = self.fire_event_for_filter(self.dest.volumes.keys())
return ''.join('{}\n'.format(name) for name in volume_names) return ''.join('{}\n'.format(name) for name in volume_names)
@qubes.api.method('admin.vm.volume.Info', no_payload=True) @qubes.api.method('admin.vm.volume.Info', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_info(self): def vm_volume_info(self):
assert self.arg in self.dest.volumes.keys() assert self.arg in self.dest.volumes.keys()
@ -265,7 +279,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}={}\n'.format(key, getattr(volume, key)) for key in return ''.join('{}={}\n'.format(key, getattr(volume, key)) for key in
volume_properties) volume_properties)
@qubes.api.method('admin.vm.volume.ListSnapshots', no_payload=True) @qubes.api.method('admin.vm.volume.ListSnapshots', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_listsnapshots(self): def vm_volume_listsnapshots(self):
assert self.arg in self.dest.volumes.keys() assert self.arg in self.dest.volumes.keys()
@ -276,7 +291,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}\n'.format(revision) for revision in revisions) return ''.join('{}\n'.format(revision) for revision in revisions)
@qubes.api.method('admin.vm.volume.Revert') @qubes.api.method('admin.vm.volume.Revert',
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_revert(self, untrusted_payload): def vm_volume_revert(self, untrusted_payload):
assert self.arg in self.dest.volumes.keys() assert self.arg in self.dest.volumes.keys()
@ -293,7 +309,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.dest.storage.get_pool(volume).revert(revision) self.dest.storage.get_pool(volume).revert(revision)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.volume.Clone') @qubes.api.method('admin.vm.volume.Clone',
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_clone(self, untrusted_payload): def vm_volume_clone(self, untrusted_payload):
assert self.arg in self.dest.volumes.keys() assert self.arg in self.dest.volumes.keys()
@ -311,7 +328,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
yield from target_vm.storage.clone_volume(self.dest, self.arg) yield from target_vm.storage.clone_volume(self.dest, self.arg)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.volume.Resize') @qubes.api.method('admin.vm.volume.Resize',
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_resize(self, untrusted_payload): def vm_volume_resize(self, untrusted_payload):
assert self.arg in self.dest.volumes.keys() assert self.arg in self.dest.volumes.keys()
@ -327,7 +345,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.dest.storage.resize(self.arg, size) self.dest.storage.resize(self.arg, size)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.volume.Import', no_payload=True) @qubes.api.method('admin.vm.volume.Import', no_payload=True,
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_volume_import(self): def vm_volume_import(self):
'''Import volume data. '''Import volume data.
@ -356,7 +375,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return '{} {}'.format(size, path) return '{} {}'.format(size, path)
@qubes.api.method('admin.vm.tag.List', no_payload=True) @qubes.api.method('admin.vm.tag.List', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_tag_list(self): def vm_tag_list(self):
assert not self.arg assert not self.arg
@ -367,7 +387,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}\n'.format(tag) for tag in sorted(tags)) return ''.join('{}\n'.format(tag) for tag in sorted(tags))
@qubes.api.method('admin.vm.tag.Get', no_payload=True) @qubes.api.method('admin.vm.tag.Get', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_tag_get(self): def vm_tag_get(self):
qubes.vm.Tags.validate_tag(self.arg) qubes.vm.Tags.validate_tag(self.arg)
@ -376,7 +397,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return '1' if self.arg in self.dest.tags else '0' return '1' if self.arg in self.dest.tags else '0'
@qubes.api.method('admin.vm.tag.Set', no_payload=True) @qubes.api.method('admin.vm.tag.Set', no_payload=True,
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_tag_set(self): def vm_tag_set(self):
qubes.vm.Tags.validate_tag(self.arg) qubes.vm.Tags.validate_tag(self.arg)
@ -386,7 +408,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.dest.tags.add(self.arg) self.dest.tags.add(self.arg)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.tag.Remove', no_payload=True) @qubes.api.method('admin.vm.tag.Remove', no_payload=True,
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_tag_remove(self): def vm_tag_remove(self):
qubes.vm.Tags.validate_tag(self.arg) qubes.vm.Tags.validate_tag(self.arg)
@ -399,7 +422,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
raise qubes.exc.QubesTagNotFoundError(self.dest, self.arg) raise qubes.exc.QubesTagNotFoundError(self.dest, self.arg)
self.app.save() self.app.save()
@qubes.api.method('admin.pool.List', no_payload=True) @qubes.api.method('admin.pool.List', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def pool_list(self): def pool_list(self):
assert not self.arg assert not self.arg
@ -409,7 +433,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}\n'.format(pool) for pool in pools) return ''.join('{}\n'.format(pool) for pool in pools)
@qubes.api.method('admin.pool.ListDrivers', no_payload=True) @qubes.api.method('admin.pool.ListDrivers', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def pool_listdrivers(self): def pool_listdrivers(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -422,7 +447,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
' '.join(qubes.storage.driver_parameters(driver))) ' '.join(qubes.storage.driver_parameters(driver)))
for driver in drivers) for driver in drivers)
@qubes.api.method('admin.pool.Info', no_payload=True) @qubes.api.method('admin.pool.Info', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def pool_info(self): def pool_info(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -435,7 +461,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}={}\n'.format(prop, val) return ''.join('{}={}\n'.format(prop, val)
for prop, val in sorted(pool.config.items())) for prop, val in sorted(pool.config.items()))
@qubes.api.method('admin.pool.Add') @qubes.api.method('admin.pool.Add',
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def pool_add(self, untrusted_payload): def pool_add(self, untrusted_payload):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -470,7 +497,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.app.add_pool(name=pool_name, driver=self.arg, **pool_config) self.app.add_pool(name=pool_name, driver=self.arg, **pool_config)
self.app.save() self.app.save()
@qubes.api.method('admin.pool.Remove', no_payload=True) @qubes.api.method('admin.pool.Remove', no_payload=True,
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def pool_remove(self): def pool_remove(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -481,7 +509,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.app.remove_pool(self.arg) self.app.remove_pool(self.arg)
self.app.save() self.app.save()
@qubes.api.method('admin.label.List', no_payload=True) @qubes.api.method('admin.label.List', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def label_list(self): def label_list(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -491,7 +520,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{}\n'.format(label.name) for label in labels) return ''.join('{}\n'.format(label.name) for label in labels)
@qubes.api.method('admin.label.Get', no_payload=True) @qubes.api.method('admin.label.Get', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def label_get(self): def label_get(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -505,7 +535,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return label.color return label.color
@qubes.api.method('admin.label.Index', no_payload=True) @qubes.api.method('admin.label.Index', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def label_index(self): def label_index(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -519,7 +550,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return str(label.index) return str(label.index)
@qubes.api.method('admin.label.Create') @qubes.api.method('admin.label.Create',
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def label_create(self, untrusted_payload): def label_create(self, untrusted_payload):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -555,7 +587,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.app.labels[new_index] = label self.app.labels[new_index] = label
self.app.save() self.app.save()
@qubes.api.method('admin.label.Remove', no_payload=True) @qubes.api.method('admin.label.Remove', no_payload=True,
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def label_remove(self): def label_remove(self):
assert self.dest.name == 'dom0' assert self.dest.name == 'dom0'
@ -577,7 +610,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
del self.app.labels[label.index] del self.app.labels[label.index]
self.app.save() self.app.save()
@qubes.api.method('admin.vm.Start', no_payload=True) @qubes.api.method('admin.vm.Start', no_payload=True,
scope='local', execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_start(self): def vm_start(self):
assert not self.arg assert not self.arg
@ -589,35 +623,40 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
raise qubes.exc.QubesException('Start failed: ' + str(e)) raise qubes.exc.QubesException('Start failed: ' + str(e))
@qubes.api.method('admin.vm.Shutdown', no_payload=True) @qubes.api.method('admin.vm.Shutdown', no_payload=True,
scope='local', execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_shutdown(self): def vm_shutdown(self):
assert not self.arg assert not self.arg
self.fire_event_for_permission() self.fire_event_for_permission()
yield from self.dest.shutdown() yield from self.dest.shutdown()
@qubes.api.method('admin.vm.Pause', no_payload=True) @qubes.api.method('admin.vm.Pause', no_payload=True,
scope='local', execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_pause(self): def vm_pause(self):
assert not self.arg assert not self.arg
self.fire_event_for_permission() self.fire_event_for_permission()
yield from self.dest.pause() yield from self.dest.pause()
@qubes.api.method('admin.vm.Unpause', no_payload=True) @qubes.api.method('admin.vm.Unpause', no_payload=True,
scope='local', execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_unpause(self): def vm_unpause(self):
assert not self.arg assert not self.arg
self.fire_event_for_permission() self.fire_event_for_permission()
yield from self.dest.unpause() yield from self.dest.unpause()
@qubes.api.method('admin.vm.Kill', no_payload=True) @qubes.api.method('admin.vm.Kill', no_payload=True,
scope='local', execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_kill(self): def vm_kill(self):
assert not self.arg assert not self.arg
self.fire_event_for_permission() self.fire_event_for_permission()
yield from self.dest.kill() yield from self.dest.kill()
@qubes.api.method('admin.Events', no_payload=True) @qubes.api.method('admin.Events', no_payload=True,
scope='global', read=True)
@asyncio.coroutine @asyncio.coroutine
def events(self): def events(self):
assert not self.arg assert not self.arg
@ -658,14 +697,16 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
else: else:
self.dest.remove_handler('*', dispatcher.vm_handler) self.dest.remove_handler('*', dispatcher.vm_handler)
@qubes.api.method('admin.vm.feature.List', no_payload=True) @qubes.api.method('admin.vm.feature.List', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_feature_list(self): def vm_feature_list(self):
assert not self.arg assert not self.arg
features = self.fire_event_for_filter(self.dest.features.keys()) features = self.fire_event_for_filter(self.dest.features.keys())
return ''.join('{}\n'.format(feature) for feature in features) return ''.join('{}\n'.format(feature) for feature in features)
@qubes.api.method('admin.vm.feature.Get', no_payload=True) @qubes.api.method('admin.vm.feature.Get', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_feature_get(self): def vm_feature_get(self):
# validation of self.arg done by qrexec-policy is enough # validation of self.arg done by qrexec-policy is enough
@ -677,7 +718,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg) raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg)
return value return value
@qubes.api.method('admin.vm.feature.CheckWithTemplate', no_payload=True) @qubes.api.method('admin.vm.feature.CheckWithTemplate', no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_feature_checkwithtemplate(self): def vm_feature_checkwithtemplate(self):
# validation of self.arg done by qrexec-policy is enough # validation of self.arg done by qrexec-policy is enough
@ -689,7 +731,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg) raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg)
return value return value
@qubes.api.method('admin.vm.feature.Remove', no_payload=True) @qubes.api.method('admin.vm.feature.Remove', no_payload=True,
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_feature_remove(self): def vm_feature_remove(self):
# validation of self.arg done by qrexec-policy is enough # validation of self.arg done by qrexec-policy is enough
@ -701,7 +744,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg) raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg)
self.app.save() self.app.save()
@qubes.api.method('admin.vm.feature.Set') @qubes.api.method('admin.vm.feature.Set',
scope='local', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_feature_set(self, untrusted_payload): def vm_feature_set(self, untrusted_payload):
# validation of self.arg done by qrexec-policy is enough # validation of self.arg done by qrexec-policy is enough
@ -713,14 +757,16 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.app.save() self.app.save()
@qubes.api.method('admin.vm.Create.{endpoint}', endpoints=(ep.name @qubes.api.method('admin.vm.Create.{endpoint}', endpoints=(ep.name
for ep in pkg_resources.iter_entry_points(qubes.vm.VM_ENTRY_POINT))) for ep in pkg_resources.iter_entry_points(qubes.vm.VM_ENTRY_POINT)),
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_create(self, endpoint, untrusted_payload=None): def vm_create(self, endpoint, untrusted_payload=None):
return self._vm_create(endpoint, allow_pool=False, return self._vm_create(endpoint, allow_pool=False,
untrusted_payload=untrusted_payload) untrusted_payload=untrusted_payload)
@qubes.api.method('admin.vm.CreateInPool.{endpoint}', endpoints=(ep.name @qubes.api.method('admin.vm.CreateInPool.{endpoint}', endpoints=(ep.name
for ep in pkg_resources.iter_entry_points(qubes.vm.VM_ENTRY_POINT))) for ep in pkg_resources.iter_entry_points(qubes.vm.VM_ENTRY_POINT)),
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_create_in_pool(self, endpoint, untrusted_payload=None): def vm_create_in_pool(self, endpoint, untrusted_payload=None):
return self._vm_create(endpoint, allow_pool=True, return self._vm_create(endpoint, allow_pool=True,
@ -810,7 +856,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
raise raise
self.app.save() self.app.save()
@qubes.api.method('admin.vm.Remove', no_payload=True) @qubes.api.method('admin.vm.Remove', no_payload=True,
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_remove(self): def vm_remove(self):
assert not self.arg assert not self.arg
@ -829,7 +876,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.app.save() self.app.save()
@qubes.api.method('admin.vm.Clone') @qubes.api.method('admin.vm.Clone',
scope='global', write=True)
@asyncio.coroutine @asyncio.coroutine
def vm_clone(self, untrusted_payload): def vm_clone(self, untrusted_payload):
assert not self.arg assert not self.arg
@ -865,7 +913,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
@qubes.api.method('admin.vm.device.{endpoint}.Available', endpoints=(ep.name @qubes.api.method('admin.vm.device.{endpoint}.Available', endpoints=(ep.name
for ep in pkg_resources.iter_entry_points('qubes.devices')), for ep in pkg_resources.iter_entry_points('qubes.devices')),
no_payload=True) no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_device_available(self, endpoint): def vm_device_available(self, endpoint):
devclass = endpoint devclass = endpoint
@ -899,7 +948,8 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
@qubes.api.method('admin.vm.device.{endpoint}.List', endpoints=(ep.name @qubes.api.method('admin.vm.device.{endpoint}.List', endpoints=(ep.name
for ep in pkg_resources.iter_entry_points('qubes.devices')), for ep in pkg_resources.iter_entry_points('qubes.devices')),
no_payload=True) no_payload=True,
scope='local', read=True)
@asyncio.coroutine @asyncio.coroutine
def vm_device_list(self, endpoint): def vm_device_list(self, endpoint):
devclass = endpoint devclass = endpoint
@ -930,8 +980,12 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
return ''.join('{} {}\n'.format(ident, dev_info[ident]) return ''.join('{} {}\n'.format(ident, dev_info[ident])
for ident in sorted(dev_info)) for ident in sorted(dev_info))
# Attach/Detach action can both modify persistent state (with
# persistent=True) and volatile state of running VM (with persistent=False).
# For this reason, write=True + execute=True
@qubes.api.method('admin.vm.device.{endpoint}.Attach', endpoints=(ep.name @qubes.api.method('admin.vm.device.{endpoint}.Attach', endpoints=(ep.name
for ep in pkg_resources.iter_entry_points('qubes.devices'))) for ep in pkg_resources.iter_entry_points('qubes.devices')),
scope='local', write=True, execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_device_attach(self, endpoint, untrusted_payload): def vm_device_attach(self, endpoint, untrusted_payload):
devclass = endpoint devclass = endpoint
@ -970,9 +1024,13 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
self.dest.devices[devclass].attach(assignment) self.dest.devices[devclass].attach(assignment)
self.app.save() self.app.save()
# Attach/Detach action can both modify persistent state (with
# persistent=True) and volatile state of running VM (with persistent=False).
# For this reason, write=True + execute=True
@qubes.api.method('admin.vm.device.{endpoint}.Detach', endpoints=(ep.name @qubes.api.method('admin.vm.device.{endpoint}.Detach', endpoints=(ep.name
for ep in pkg_resources.iter_entry_points('qubes.devices')), for ep in pkg_resources.iter_entry_points('qubes.devices')),
no_payload=True) no_payload=True,
scope='local', write=True, execute=True)
@asyncio.coroutine @asyncio.coroutine
def vm_device_detach(self, endpoint): def vm_device_detach(self, endpoint):
devclass = endpoint devclass = endpoint