ext/core-features: make 'template-postinstall' event async
It makes a lot of sense to call long-running operations in that event handler, including calling back into the VM. Allow that by using fire_event_async, not just fire_event. Also, document the event.
This commit is contained in:
parent
d2585aa871
commit
0eab082d85
@ -17,12 +17,14 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
# License along with this library; if not, see <https://www.gnu.org/licenses/>.
|
# License along with this library; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
import asyncio
|
||||||
|
|
||||||
import qubes.ext
|
import qubes.ext
|
||||||
|
|
||||||
class CoreFeatures(qubes.ext.Extension):
|
class CoreFeatures(qubes.ext.Extension):
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
@qubes.ext.handler('features-request')
|
@qubes.ext.handler('features-request')
|
||||||
|
@asyncio.coroutine
|
||||||
def qubes_features_request(self, vm, event, untrusted_features):
|
def qubes_features_request(self, vm, event, untrusted_features):
|
||||||
'''Handle features provided by qubes-core-agent and qubes-gui-agent'''
|
'''Handle features provided by qubes-core-agent and qubes-gui-agent'''
|
||||||
# pylint: disable=no-self-use,unused-argument
|
# pylint: disable=no-self-use,unused-argument
|
||||||
@ -58,4 +60,4 @@ class CoreFeatures(qubes.ext.Extension):
|
|||||||
if not qrexec_before and vm.features.get('qrexec', False):
|
if not qrexec_before and vm.features.get('qrexec', False):
|
||||||
# if this is the first time qrexec was advertised, now can finish
|
# if this is the first time qrexec was advertised, now can finish
|
||||||
# template setup
|
# template setup
|
||||||
vm.fire_event('template-postinstall')
|
yield from vm.fire_event_async('template-postinstall')
|
||||||
|
@ -40,12 +40,13 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
def test_010_notify_tools(self):
|
def test_010_notify_tools(self):
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'gui': '1',
|
untrusted_features={
|
||||||
'version': '1',
|
'gui': '1',
|
||||||
'default-user': 'user',
|
'version': '1',
|
||||||
'qrexec': '1'}),
|
'default-user': 'user',
|
||||||
|
'qrexec': '1'}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.__contains__', ('qrexec',), {}),
|
('features.__contains__', ('qrexec',), {}),
|
||||||
@ -53,17 +54,19 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
('features.__contains__', ('gui',), {}),
|
('features.__contains__', ('gui',), {}),
|
||||||
('features.__setitem__', ('gui', True), {}),
|
('features.__setitem__', ('gui', True), {}),
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('fire_event', ('template-postinstall',), {})
|
('fire_event_async', ('template-postinstall',), {}),
|
||||||
|
('fire_event_async().__iter__', (), {}),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_011_notify_tools_uninstall(self):
|
def test_011_notify_tools_uninstall(self):
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'gui': '0',
|
untrusted_features={
|
||||||
'version': '1',
|
'gui': '0',
|
||||||
'default-user': 'user',
|
'version': '1',
|
||||||
'qrexec': '0'}),
|
'default-user': 'user',
|
||||||
|
'qrexec': '0'}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.__contains__', ('qrexec',), {}),
|
('features.__contains__', ('qrexec',), {}),
|
||||||
@ -75,11 +78,12 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
def test_012_notify_tools_uninstall2(self):
|
def test_012_notify_tools_uninstall2(self):
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'version': '1',
|
untrusted_features={
|
||||||
'default-user': 'user',
|
'version': '1',
|
||||||
})
|
'default-user': 'user',
|
||||||
|
}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
@ -87,12 +91,13 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
def test_013_notify_tools_no_version(self):
|
def test_013_notify_tools_no_version(self):
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'qrexec': '1',
|
untrusted_features={
|
||||||
'gui': '1',
|
'qrexec': '1',
|
||||||
'default-user': 'user',
|
'gui': '1',
|
||||||
})
|
'default-user': 'user',
|
||||||
|
}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.__contains__', ('qrexec',), {}),
|
('features.__contains__', ('qrexec',), {}),
|
||||||
@ -100,18 +105,20 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
('features.__contains__', ('gui',), {}),
|
('features.__contains__', ('gui',), {}),
|
||||||
('features.__setitem__', ('gui', True), {}),
|
('features.__setitem__', ('gui', True), {}),
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('fire_event', ('template-postinstall',), {})
|
('fire_event_async', ('template-postinstall',), {}),
|
||||||
|
('fire_event_async().__iter__', (), {}),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_015_notify_tools_invalid_value_qrexec(self):
|
def test_015_notify_tools_invalid_value_qrexec(self):
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'version': '1',
|
untrusted_features={
|
||||||
'qrexec': 'invalid',
|
'version': '1',
|
||||||
'gui': '1',
|
'qrexec': 'invalid',
|
||||||
'default-user': 'user',
|
'gui': '1',
|
||||||
})
|
'default-user': 'user',
|
||||||
|
}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.__contains__', ('gui',), {}),
|
('features.__contains__', ('gui',), {}),
|
||||||
@ -121,29 +128,32 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
def test_016_notify_tools_invalid_value_gui(self):
|
def test_016_notify_tools_invalid_value_gui(self):
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'version': '1',
|
untrusted_features={
|
||||||
'qrexec': '1',
|
'version': '1',
|
||||||
'gui': 'invalid',
|
'qrexec': '1',
|
||||||
'default-user': 'user',
|
'gui': 'invalid',
|
||||||
})
|
'default-user': 'user',
|
||||||
|
}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.__contains__', ('qrexec',), {}),
|
('features.__contains__', ('qrexec',), {}),
|
||||||
('features.__setitem__', ('qrexec', True), {}),
|
('features.__setitem__', ('qrexec', True), {}),
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('fire_event', ('template-postinstall',), {})
|
('fire_event_async', ('template-postinstall',), {}),
|
||||||
|
('fire_event_async().__iter__', (), {}),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_017_notify_tools_template_based(self):
|
def test_017_notify_tools_template_based(self):
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'version': '1',
|
untrusted_features={
|
||||||
'qrexec': '1',
|
'version': '1',
|
||||||
'gui': '1',
|
'qrexec': '1',
|
||||||
'default-user': 'user',
|
'gui': '1',
|
||||||
})
|
'default-user': 'user',
|
||||||
|
}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('template.__bool__', (), {}),
|
('template.__bool__', (), {}),
|
||||||
('log.warning', ('Ignoring qubes.NotifyTools for template-based '
|
('log.warning', ('Ignoring qubes.NotifyTools for template-based '
|
||||||
@ -154,12 +164,13 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
self.features['qrexec'] = True
|
self.features['qrexec'] = True
|
||||||
self.features['gui'] = True
|
self.features['gui'] = True
|
||||||
del self.vm.template
|
del self.vm.template
|
||||||
self.ext.qubes_features_request(self.vm, 'features-request',
|
self.loop.run_until_complete(
|
||||||
untrusted_features={
|
self.ext.qubes_features_request(self.vm, 'features-request',
|
||||||
'gui': '1',
|
untrusted_features={
|
||||||
'version': '1',
|
'gui': '1',
|
||||||
'default-user': 'user',
|
'version': '1',
|
||||||
'qrexec': '1'}),
|
'default-user': 'user',
|
||||||
|
'qrexec': '1'}))
|
||||||
self.assertEqual(self.vm.mock_calls, [
|
self.assertEqual(self.vm.mock_calls, [
|
||||||
('features.get', ('qrexec', False), {}),
|
('features.get', ('qrexec', False), {}),
|
||||||
('features.__contains__', ('qrexec',), {}),
|
('features.__contains__', ('qrexec',), {}),
|
||||||
|
@ -413,6 +413,15 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
On the `vm` object there was probably ``property-set:netvm`` fired
|
On the `vm` object there was probably ``property-set:netvm`` fired
|
||||||
earlier.
|
earlier.
|
||||||
|
|
||||||
|
.. event:: template-postinstall (subject, event)
|
||||||
|
|
||||||
|
Fired on non-template-based domain (TemplateVM, StandaloneVM) when
|
||||||
|
it first reports qrexec presence. This happens at the first
|
||||||
|
domain startup just after its installation and is suitable for
|
||||||
|
performing various post-installation setup.
|
||||||
|
|
||||||
|
Handler for this event can be asynchronous (a coroutine).
|
||||||
'''
|
'''
|
||||||
|
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user