Merge remote-tracking branch 'origin/pr/342'
* origin/pr/342: Added a servicevm feature extension Removed unused Qubes Manager extension Add vm.icon property
This commit is contained in:
commit
c7d3635972
@ -62,3 +62,25 @@ class CoreFeatures(qubes.ext.Extension):
|
|||||||
# 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
|
||||||
yield from vm.fire_event_async('template-postinstall')
|
yield from vm.fire_event_async('template-postinstall')
|
||||||
|
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
def set_servicevm_feature(self, subject):
|
||||||
|
if getattr(subject, 'provides_network', False):
|
||||||
|
subject.features['servicevm'] = 1
|
||||||
|
elif 'servicevm' in subject.features:
|
||||||
|
del subject.features['servicevm']
|
||||||
|
|
||||||
|
@qubes.ext.handler('property-set:provides_network')
|
||||||
|
def on_property_set(self, subject, event, name, newvalue, oldvalue=None):
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
self.set_servicevm_feature(subject)
|
||||||
|
|
||||||
|
@qubes.ext.handler('property-del:provides_network')
|
||||||
|
def on_property_del(self, subject, event, name):
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
self.set_servicevm_feature(subject)
|
||||||
|
|
||||||
|
@qubes.ext.handler('domain-load')
|
||||||
|
def on_domain_load(self, subject, event):
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
self.set_servicevm_feature(subject)
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
#
|
|
||||||
# The Qubes OS Project, https://www.qubes-os.org/
|
|
||||||
#
|
|
||||||
# Copyright (C) 2014-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
|
||||||
# Copyright (C) 2014 Marek Marczykowski-Górecki
|
|
||||||
# <marmarek@invisiblethingslab.com>
|
|
||||||
# Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2.1 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
|
|
||||||
'''Qubes Manager hooks.
|
|
||||||
|
|
||||||
.. warning:: API defined here is not declared stable.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
import qubes.ext
|
|
||||||
|
|
||||||
|
|
||||||
class QubesManager(qubes.ext.Extension):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(QubesManager, self).__init__(*args, **kwargs)
|
|
||||||
try:
|
|
||||||
self._system_bus = dbus.SystemBus()
|
|
||||||
except dbus.exceptions.DBusException:
|
|
||||||
# we can't access Qubes() object here to check for offline mode,
|
|
||||||
# so lets assume it is this case...
|
|
||||||
self._system_bus = None
|
|
||||||
|
|
||||||
# pylint: disable=no-self-use,unused-argument,too-few-public-methods
|
|
||||||
|
|
||||||
@qubes.ext.handler('status:error')
|
|
||||||
def on_status_error(self, vm, event, status, message):
|
|
||||||
if self._system_bus is None:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
qubes_manager = self._system_bus.get_object(
|
|
||||||
'org.qubesos.QubesManager',
|
|
||||||
'/org/qubesos/QubesManager')
|
|
||||||
qubes_manager.notify_error(vm.name, message,
|
|
||||||
dbus_interface='org.qubesos.QubesManager')
|
|
||||||
except dbus.DBusException:
|
|
||||||
# ignore the case when no qubes-manager is running
|
|
||||||
pass
|
|
||||||
|
|
||||||
@qubes.ext.handler('status:no-error')
|
|
||||||
def on_status_no_error(self, vm, event, status, message):
|
|
||||||
if self._system_bus is None:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
qubes_manager = self._system_bus.get_object(
|
|
||||||
'org.qubesos.QubesManager',
|
|
||||||
'/org/qubesos/QubesManager')
|
|
||||||
qubes_manager.clear_error_exact(vm.name, message,
|
|
||||||
dbus_interface='org.qubesos.QubesManager')
|
|
||||||
except dbus.DBusException:
|
|
||||||
# ignore the case when no qubes-manager is running
|
|
||||||
pass
|
|
@ -34,8 +34,11 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
self.features = {}
|
self.features = {}
|
||||||
self.vm.configure_mock(**{
|
self.vm.configure_mock(**{
|
||||||
'features.get.side_effect': self.features.get,
|
'features.get.side_effect': self.features.get,
|
||||||
|
'features.items.side_effect': self.features.items,
|
||||||
|
'features.__iter__.side_effect': self.features.__iter__,
|
||||||
'features.__contains__.side_effect': self.features.__contains__,
|
'features.__contains__.side_effect': self.features.__contains__,
|
||||||
'features.__setitem__.side_effect': self.features.__setitem__,
|
'features.__setitem__.side_effect': self.features.__setitem__,
|
||||||
|
'features.__delitem__.side_effect': self.features.__delitem__,
|
||||||
})
|
})
|
||||||
|
|
||||||
def test_010_notify_tools(self):
|
def test_010_notify_tools(self):
|
||||||
@ -181,6 +184,16 @@ class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
|
|||||||
('features.__contains__', ('gui',), {}),
|
('features.__contains__', ('gui',), {}),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_100_servicevm_feature(self):
|
||||||
|
self.vm.provides_network = True
|
||||||
|
self.ext.set_servicevm_feature(self.vm)
|
||||||
|
self.assertEqual(self.features['servicevm'], 1)
|
||||||
|
|
||||||
|
self.vm.provides_network = False
|
||||||
|
self.ext.set_servicevm_feature(self.vm)
|
||||||
|
self.assertNotIn('servicevm', self.features)
|
||||||
|
|
||||||
|
|
||||||
class TC_10_WindowsFeatures(qubes.tests.QubesTestCase):
|
class TC_10_WindowsFeatures(qubes.tests.QubesTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
@ -65,7 +65,10 @@ class TestPool(object):
|
|||||||
return TestVolume(self)
|
return TestVolume(self)
|
||||||
|
|
||||||
class TestApp(qubes.tests.TestEmitter):
|
class TestApp(qubes.tests.TestEmitter):
|
||||||
labels = {1: qubes.Label(1, '0xcc0000', 'red')}
|
labels = {1: qubes.Label(1, '0xcc0000', 'red'),
|
||||||
|
2: qubes.Label(2, '0x00cc00', 'green'),
|
||||||
|
3: qubes.Label(3, '0x0000cc', 'blue'),
|
||||||
|
4: qubes.Label(4, '0xcccccc', 'black')}
|
||||||
check_updates_vm = False
|
check_updates_vm = False
|
||||||
|
|
||||||
def get_label(self, label):
|
def get_label(self, label):
|
||||||
|
@ -47,7 +47,10 @@ import qubes.tests
|
|||||||
import qubes.tests.vm
|
import qubes.tests.vm
|
||||||
|
|
||||||
class TestApp(object):
|
class TestApp(object):
|
||||||
labels = {1: qubes.Label(1, '0xcc0000', 'red')}
|
labels = {1: qubes.Label(1, '0xcc0000', 'red'),
|
||||||
|
2: qubes.Label(2, '0x00cc00', 'green'),
|
||||||
|
3: qubes.Label(3, '0x0000cc', 'blue'),
|
||||||
|
4: qubes.Label(4, '0xcccccc', 'black')}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.domains = {}
|
self.domains = {}
|
||||||
@ -394,24 +397,46 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
|
|||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
vm.uuid = uuid.uuid4()
|
vm.uuid = uuid.uuid4()
|
||||||
|
|
||||||
@unittest.skip('TODO: how to not fail on making an icon symlink here?')
|
@unittest.mock.patch("os.symlink")
|
||||||
def test_130_label(self):
|
def test_130_label(self, _):
|
||||||
vm = self.get_vm()
|
vm = self.get_vm()
|
||||||
self.assertPropertyDefaultValue(vm, 'label')
|
self.assertPropertyDefaultValue(vm, 'label')
|
||||||
self.assertPropertyValue(vm, 'label', self.app.labels[1],
|
self.assertPropertyValue(vm, 'label', self.app.labels[1],
|
||||||
self.app.labels[1], 'label-1')
|
self.app.labels[1], 'red')
|
||||||
del vm.label
|
del vm.label
|
||||||
self.assertPropertyDefaultValue(vm, 'label')
|
self.assertPropertyDefaultValue(vm, 'label')
|
||||||
self.assertPropertyValue(vm, 'label', 'red',
|
self.assertPropertyValue(vm, 'label', 'red',
|
||||||
self.app.labels[1], 'label-1')
|
self.app.labels[1], 'red')
|
||||||
self.assertPropertyValue(vm, 'label', 'label-1',
|
self.assertPropertyValue(vm, 'label', 'label-1',
|
||||||
self.app.labels[1], 'label-1')
|
self.app.labels[1], 'red')
|
||||||
|
|
||||||
def test_131_label_invalid(self):
|
def test_131_label_invalid(self):
|
||||||
vm = self.get_vm()
|
vm = self.get_vm()
|
||||||
self.assertPropertyInvalidValue(vm, 'label', 'invalid')
|
self.assertPropertyInvalidValue(vm, 'label', 'invalid')
|
||||||
self.assertPropertyInvalidValue(vm, 'label', 123)
|
self.assertPropertyInvalidValue(vm, 'label', 123)
|
||||||
|
|
||||||
|
@unittest.mock.patch("os.symlink")
|
||||||
|
def test_135_icon(self, _):
|
||||||
|
vm = self.get_vm(cls=qubes.vm.appvm.AppVM)
|
||||||
|
vm.label = "red"
|
||||||
|
self.assertEqual(vm.icon, "appvm-red")
|
||||||
|
|
||||||
|
templatevm = self.get_vm(cls=qubes.vm.templatevm.TemplateVM)
|
||||||
|
templatevm.label = "blue"
|
||||||
|
self.assertEqual(templatevm.icon, "templatevm-blue")
|
||||||
|
|
||||||
|
vm.template_for_dispvms = True
|
||||||
|
dispvm = self.get_vm(cls=qubes.vm.dispvm.DispVM, template=vm,
|
||||||
|
dispid=10)
|
||||||
|
dispvm.label = "black"
|
||||||
|
self.assertEqual(dispvm.icon, "dispvm-black")
|
||||||
|
|
||||||
|
vm = self.get_vm()
|
||||||
|
vm.label = "green"
|
||||||
|
vm.features["servicevm"] = 1
|
||||||
|
self.assertEqual(vm.icon, "servicevm-green")
|
||||||
|
|
||||||
|
|
||||||
def test_160_memory(self):
|
def test_160_memory(self):
|
||||||
vm = self.get_vm()
|
vm = self.get_vm()
|
||||||
self.assertPropertyDefaultValue(vm, 'memory', 400)
|
self.assertPropertyDefaultValue(vm, 'memory', 400)
|
||||||
|
@ -2101,6 +2101,20 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@qubes.stateless_property
|
||||||
|
def icon(self):
|
||||||
|
"""freedesktop icon name, suitable for use in
|
||||||
|
:py:meth:`PyQt4.QtGui.QIcon.fromTheme`"""
|
||||||
|
# pylint: disable=comparison-with-callable
|
||||||
|
raw_icon_name = self.label.name
|
||||||
|
if self.klass == 'TemplateVM':
|
||||||
|
return 'templatevm-' + raw_icon_name
|
||||||
|
if self.klass == 'DispVM':
|
||||||
|
return 'dispvm-' + raw_icon_name
|
||||||
|
if self.features.get('servicevm', False):
|
||||||
|
return 'servicevm-' + raw_icon_name
|
||||||
|
return 'appvm-' + raw_icon_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kernelopts_common(self):
|
def kernelopts_common(self):
|
||||||
"""Kernel options which should be used in addition to *kernelopts*
|
"""Kernel options which should be used in addition to *kernelopts*
|
||||||
|
@ -277,7 +277,6 @@ fi
|
|||||||
%{python3_sitelib}/qubes/ext/gui.py
|
%{python3_sitelib}/qubes/ext/gui.py
|
||||||
%{python3_sitelib}/qubes/ext/audio.py
|
%{python3_sitelib}/qubes/ext/audio.py
|
||||||
%{python3_sitelib}/qubes/ext/pci.py
|
%{python3_sitelib}/qubes/ext/pci.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
|
%{python3_sitelib}/qubes/ext/services.py
|
||||||
%{python3_sitelib}/qubes/ext/windows.py
|
%{python3_sitelib}/qubes/ext/windows.py
|
||||||
|
1
setup.py
1
setup.py
@ -62,7 +62,6 @@ if __name__ == '__main__':
|
|||||||
'qubes.ext': [
|
'qubes.ext': [
|
||||||
'qubes.ext.admin = qubes.ext.admin:AdminExtension',
|
'qubes.ext.admin = qubes.ext.admin:AdminExtension',
|
||||||
'qubes.ext.core_features = qubes.ext.core_features:CoreFeatures',
|
'qubes.ext.core_features = qubes.ext.core_features:CoreFeatures',
|
||||||
'qubes.ext.qubesmanager = qubes.ext.qubesmanager:QubesManager',
|
|
||||||
'qubes.ext.gui = qubes.ext.gui:GUI',
|
'qubes.ext.gui = qubes.ext.gui:GUI',
|
||||||
'qubes.ext.audio = qubes.ext.audio:AUDIO',
|
'qubes.ext.audio = qubes.ext.audio:AUDIO',
|
||||||
'qubes.ext.r3compatibility = qubes.ext.r3compatibility:R3Compatibility',
|
'qubes.ext.r3compatibility = qubes.ext.r3compatibility:R3Compatibility',
|
||||||
|
Loading…
Reference in New Issue
Block a user