Merge branch 'services'
* services: tests: check clockvm-related handlers doc: include list of extensions qubesvm: fix docstring ext/services: move exporting 'service.*' features to extensions app: update handling features/service os ClockVM
This commit is contained in:
		
						commit
						36f1a3abaf
					
				@ -1,8 +1,22 @@
 | 
				
			|||||||
:py:mod:`qubes.ext` -- Qubes extensions
 | 
					:py:mod:`qubes.ext` -- Qubes extensions
 | 
				
			||||||
=======================================
 | 
					=======================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module contents
 | 
				
			||||||
 | 
					---------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. automodule:: qubes.ext
 | 
					.. automodule:: qubes.ext
 | 
				
			||||||
   :members:
 | 
					   :members:
 | 
				
			||||||
   :show-inheritance:
 | 
					   :show-inheritance:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Extensions defined here
 | 
				
			||||||
 | 
					-----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.admin.AdminExtension
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.block.BlockDeviceExtension
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.core_features.CoreFeatures
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.gui.GUI
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.pci.PCIDeviceExtension
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.r3compatibility.R3Compatibility
 | 
				
			||||||
 | 
					.. autoclass:: qubes.ext.services.ServicesExtension
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. vim: ts=3 sw=3 et
 | 
					.. vim: ts=3 sw=3 et
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								qubes/app.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								qubes/app.py
									
									
									
									
									
								
							@ -680,7 +680,7 @@ class Qubes(qubes.PropertyHolder):
 | 
				
			|||||||
        doc='''Which VM to use as `yum` proxy for updating AdminVM and
 | 
					        doc='''Which VM to use as `yum` proxy for updating AdminVM and
 | 
				
			||||||
            TemplateVMs''')
 | 
					            TemplateVMs''')
 | 
				
			||||||
    clockvm = qubes.VMProperty('clockvm', load_stage=3,
 | 
					    clockvm = qubes.VMProperty('clockvm', load_stage=3,
 | 
				
			||||||
        allow_none=True,
 | 
					        default=None, allow_none=True,
 | 
				
			||||||
        doc='Which VM to use as NTP proxy for updating AdminVM')
 | 
					        doc='Which VM to use as NTP proxy for updating AdminVM')
 | 
				
			||||||
    default_kernel = qubes.property('default_kernel', load_stage=3,
 | 
					    default_kernel = qubes.property('default_kernel', load_stage=3,
 | 
				
			||||||
        doc='Which kernel to use when not overriden in VM')
 | 
					        doc='Which kernel to use when not overriden in VM')
 | 
				
			||||||
@ -830,17 +830,6 @@ class Qubes(qubes.PropertyHolder):
 | 
				
			|||||||
        self.property_require('clockvm', allow_none=True)
 | 
					        self.property_require('clockvm', allow_none=True)
 | 
				
			||||||
        self.property_require('updatevm', allow_none=True)
 | 
					        self.property_require('updatevm', allow_none=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Disable ntpd in ClockVM - to not conflict with ntpdate (both are
 | 
					 | 
				
			||||||
        # using 123/udp port)
 | 
					 | 
				
			||||||
        if hasattr(self, 'clockvm') and self.clockvm is not None:
 | 
					 | 
				
			||||||
            if self.clockvm.features.get('service.ntpd', False):
 | 
					 | 
				
			||||||
                self.log.warning(
 | 
					 | 
				
			||||||
                    'VM set as clockvm (%r) has enabled \'ntpd\' service! '
 | 
					 | 
				
			||||||
                    'Expect failure when syncing time in dom0.',
 | 
					 | 
				
			||||||
                    self.clockvm)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                self.clockvm.features['service.ntpd'] = ''
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for vm in self.domains:
 | 
					        for vm in self.domains:
 | 
				
			||||||
            vm.events_enabled = True
 | 
					            vm.events_enabled = True
 | 
				
			||||||
            vm.fire_event('domain-load')
 | 
					            vm.fire_event('domain-load')
 | 
				
			||||||
@ -1173,13 +1162,14 @@ class Qubes(qubes.PropertyHolder):
 | 
				
			|||||||
        # pylint: disable=unused-argument,no-self-use
 | 
					        # pylint: disable=unused-argument,no-self-use
 | 
				
			||||||
        if newvalue is None:
 | 
					        if newvalue is None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        if newvalue.features.get('service.ntpd', False):
 | 
					        if 'service.clocksync' not in newvalue.features:
 | 
				
			||||||
            raise qubes.exc.QubesVMError(newvalue,
 | 
					            newvalue.features['service.clocksync'] = True
 | 
				
			||||||
                'Cannot set {!r} as {!r} since it has ntpd enabled.'.format(
 | 
					 | 
				
			||||||
                    newvalue.name, name))
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            newvalue.features['service.ntpd'] = ''
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @qubes.events.handler('property-set:clockvm')
 | 
				
			||||||
 | 
					    def on_property_set_clockvm(self, event, name, newvalue, oldvalue=None):
 | 
				
			||||||
 | 
					        # pylint: disable=unused-argument,no-self-use
 | 
				
			||||||
 | 
					        if oldvalue and oldvalue.features.get('service.clocksync', False):
 | 
				
			||||||
 | 
					            del oldvalue.features['service.clocksync']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @qubes.events.handler(
 | 
					    @qubes.events.handler(
 | 
				
			||||||
        'property-pre-set:default_netvm',
 | 
					        'property-pre-set:default_netvm',
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										62
									
								
								qubes/ext/services.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								qubes/ext/services.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					# -*- encoding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The Qubes OS Project, http://www.qubes-os.org
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (C) 2017 Marek Marczykowski-Górecki
 | 
				
			||||||
 | 
					#                               <marmarek@invisiblethingslab.com>
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License along
 | 
				
			||||||
 | 
					# with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					'''Extension responsible for qvm-service framework'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import qubes.ext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServicesExtension(qubes.ext.Extension):
 | 
				
			||||||
 | 
					    '''This extension export features with 'service.' prefix to QubesDB in
 | 
				
			||||||
 | 
					    /qubes-service/ tree.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    # pylint: disable=no-self-use
 | 
				
			||||||
 | 
					    @qubes.ext.handler('domain-qdb-create')
 | 
				
			||||||
 | 
					    def on_domain_qdb_create(self, vm):
 | 
				
			||||||
 | 
					        '''Actually export features'''
 | 
				
			||||||
 | 
					        for feature, value in vm.features.items():
 | 
				
			||||||
 | 
					            if not feature.startswith('service.'):
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            service = feature[len('service.'):]
 | 
				
			||||||
 | 
					            # forcefully convert to '0' or '1'
 | 
				
			||||||
 | 
					            vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
 | 
				
			||||||
 | 
					                str(int(bool(value))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @qubes.ext.handler('domain-feature-set')
 | 
				
			||||||
 | 
					    def on_domain_feature_set(self, vm, feature, value, oldvalue=None):
 | 
				
			||||||
 | 
					        '''Update /qubes-service/ QubesDB tree in runtime'''
 | 
				
			||||||
 | 
					        # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					        if not vm.is_running():
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        if not feature.startswith('service.'):
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        service = feature[len('service.'):]
 | 
				
			||||||
 | 
					        # forcefully convert to '0' or '1'
 | 
				
			||||||
 | 
					        vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
 | 
				
			||||||
 | 
					            str(int(bool(value))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @qubes.ext.handler('domain-feature-delete')
 | 
				
			||||||
 | 
					    def on_domain_feature_delete(self, vm, feature):
 | 
				
			||||||
 | 
					        '''Update /qubes-service/ QubesDB tree in runtime'''
 | 
				
			||||||
 | 
					        if not vm.is_running():
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        if not feature.startswith('service.'):
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        service = feature[len('service.'):]
 | 
				
			||||||
 | 
					        vm.untrusted_qdb.rm('/qubes-service/{}'.format(service))
 | 
				
			||||||
@ -261,15 +261,41 @@ class TC_30_VMCollection(qubes.tests.QubesTestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TC_90_Qubes(qubes.tests.QubesTestCase):
 | 
					class TC_90_Qubes(qubes.tests.QubesTestCase):
 | 
				
			||||||
 | 
					    def tearDown(self):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            os.unlink('/tmp/qubestest.xml')
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        super(TC_90_Qubes, self).tearDown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @qubes.tests.skipUnlessDom0
 | 
					    @qubes.tests.skipUnlessDom0
 | 
				
			||||||
    def test_000_init_empty(self):
 | 
					    def test_000_init_empty(self):
 | 
				
			||||||
        # pylint: disable=no-self-use,unused-variable,bare-except
 | 
					        # pylint: disable=no-self-use,unused-variable,bare-except
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            os.unlink('/tmp/qubestest.xml')
 | 
					            os.unlink('/tmp/qubestest.xml')
 | 
				
			||||||
        except:
 | 
					        except FileNotFoundError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
        qubes.Qubes.create_empty_store('/tmp/qubestest.xml')
 | 
					        qubes.Qubes.create_empty_store('/tmp/qubestest.xml')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_100_clockvm(self):
 | 
				
			||||||
 | 
					        app = qubes.Qubes('/tmp/qubestest.xml', load=False, offline_mode=True)
 | 
				
			||||||
 | 
					        app.load_initial_values()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        template = app.add_new_vm('TemplateVM', name='test-template',
 | 
				
			||||||
 | 
					            label='green')
 | 
				
			||||||
 | 
					        appvm = app.add_new_vm('AppVM', name='test-vm', template=template,
 | 
				
			||||||
 | 
					            label='red')
 | 
				
			||||||
 | 
					        self.assertIsNone(app.clockvm)
 | 
				
			||||||
 | 
					        self.assertNotIn('service.clocksync', appvm.features)
 | 
				
			||||||
 | 
					        self.assertNotIn('service.clocksync', template.features)
 | 
				
			||||||
 | 
					        app.clockvm = appvm
 | 
				
			||||||
 | 
					        self.assertIn('service.clocksync', appvm.features)
 | 
				
			||||||
 | 
					        self.assertTrue(appvm.features['service.clocksync'])
 | 
				
			||||||
 | 
					        app.clockvm = template
 | 
				
			||||||
 | 
					        self.assertNotIn('service.clocksync', appvm.features)
 | 
				
			||||||
 | 
					        self.assertIn('service.clocksync', template.features)
 | 
				
			||||||
 | 
					        self.assertTrue(template.features['service.clocksync'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @qubes.tests.skipUnlessGit
 | 
					    @qubes.tests.skipUnlessGit
 | 
				
			||||||
    def test_900_example_xml_in_doc(self):
 | 
					    def test_900_example_xml_in_doc(self):
 | 
				
			||||||
        self.assertXMLIsValid(
 | 
					        self.assertXMLIsValid(
 | 
				
			||||||
 | 
				
			|||||||
@ -289,7 +289,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
				
			|||||||
            :param value: new value
 | 
					            :param value: new value
 | 
				
			||||||
            :param oldvalue: old value, if any
 | 
					            :param oldvalue: old value, if any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .. event:: domain-feature-delete (subject, event, key)
 | 
					        .. event:: domain-feature-delete (subject, event, feature)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            A feature was removed.
 | 
					            A feature was removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1746,14 +1746,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
				
			|||||||
        if tzname:
 | 
					        if tzname:
 | 
				
			||||||
            self.untrusted_qdb.write('/qubes-timezone', tzname)
 | 
					            self.untrusted_qdb.write('/qubes-timezone', tzname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for feature, value in self.features.items():
 | 
					 | 
				
			||||||
            if not feature.startswith('service.'):
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            service = feature[len('service.'):]
 | 
					 | 
				
			||||||
            # forcefully convert to '0' or '1'
 | 
					 | 
				
			||||||
            self.untrusted_qdb.write('/qubes-service/{}'.format(service),
 | 
					 | 
				
			||||||
                str(int(bool(value))))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.untrusted_qdb.write('/qubes-block-devices', '')
 | 
					        self.untrusted_qdb.write('/qubes-block-devices', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.untrusted_qdb.write('/qubes-usb-devices', '')
 | 
					        self.untrusted_qdb.write('/qubes-usb-devices', '')
 | 
				
			||||||
 | 
				
			|||||||
@ -294,6 +294,7 @@ fi
 | 
				
			|||||||
%{python3_sitelib}/qubes/ext/pci.py
 | 
					%{python3_sitelib}/qubes/ext/pci.py
 | 
				
			||||||
%{python3_sitelib}/qubes/ext/qubesmanager.py
 | 
					%{python3_sitelib}/qubes/ext/qubesmanager.py
 | 
				
			||||||
%{python3_sitelib}/qubes/ext/r3compatibility.py
 | 
					%{python3_sitelib}/qubes/ext/r3compatibility.py
 | 
				
			||||||
 | 
					%{python3_sitelib}/qubes/ext/services.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
%dir %{python3_sitelib}/qubes/tests
 | 
					%dir %{python3_sitelib}/qubes/tests
 | 
				
			||||||
%dir %{python3_sitelib}/qubes/tests/__pycache__
 | 
					%dir %{python3_sitelib}/qubes/tests/__pycache__
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user