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