Make some properties default to template's value (if any)

Multiple properties are related to system installed inside the VM, so it
makes sense to have them the same for all the VMs based on the same
template. Modify default value getter to first try get the value from a
template (if any) and only if it fails, fallback to original default
value.
This change is made to those properties:
 - default_user (it was already this way)
 - kernel
 - kernelopts
 - maxmem
 - memory
 - qrexec_timeout
 - vcpus
 - virt_mode

This is especially useful for manually installed templates (like
Windows).

Related to QubesOS/qubes-issues#3585
This commit is contained in:
Marek Marczykowski-Górecki 2018-07-11 04:35:36 +02:00
parent af7d54d388
commit af2435c0d4
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
2 changed files with 86 additions and 16 deletions

View File

@ -141,6 +141,50 @@ class TC_00_setters(qubes.tests.QubesTestCase):
with self.assertRaises(ValueError):
qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'True')
class TC_10_default(qubes.tests.QubesTestCase):
def setUp(self):
super().setUp()
self.app = TestApp()
self.vm = TestVM(app=self.app)
self.prop = TestProp()
def test_000_default_with_template_simple(self):
default_getter = qubes.vm.qubesvm._default_with_template('kernel',
'dfl-kernel')
self.assertEqual(default_getter(self.vm), 'dfl-kernel')
self.vm.template = None
self.assertEqual(default_getter(self.vm), 'dfl-kernel')
self.vm.template = unittest.mock.Mock()
self.vm.template.kernel = 'template-kernel'
self.assertEqual(default_getter(self.vm), 'template-kernel')
def test_001_default_with_template_callable(self):
default_getter = qubes.vm.qubesvm._default_with_template('kernel',
lambda x: x.app.default_kernel)
self.app.default_kernel = 'global-dfl-kernel'
self.assertEqual(default_getter(self.vm), 'global-dfl-kernel')
self.vm.template = None
self.assertEqual(default_getter(self.vm), 'global-dfl-kernel')
self.vm.template = unittest.mock.Mock()
self.vm.template.kernel = 'template-kernel'
self.assertEqual(default_getter(self.vm), 'template-kernel')
def test_010_default_virt_mode(self):
default_getter = qubes.vm.qubesvm._default_with_template('kernel',
lambda x: x.app.default_kernel)
self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
'pvh')
self.vm.template = unittest.mock.Mock()
self.vm.template.virt_mode = 'hvm'
self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
'hvm')
self.vm.template = None
self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
'pvh')
self.vm.devices['pci'].persistent().append('some-dev')
self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
'hvm')
class QubesVMTestsMixin(object):
property_no_default = object()

View File

@ -102,7 +102,25 @@ def _setter_virt_mode(self, prop, value):
def _default_virt_mode(self):
if self.devices['pci'].persistent():
return 'hvm'
return 'pvh'
try:
return self.template.virt_mode
except AttributeError:
return 'pvh'
def _default_with_template(prop, default):
'''Return a callable for 'default' argument of a property. Use a value
from a template (if any), otherwise *default*
'''
def _func(self):
try:
return getattr(self.template, prop)
except AttributeError:
if callable(default):
return default(self)
return default
return _func
class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
@ -387,7 +405,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
type=str, setter=_setter_virt_mode,
default=_default_virt_mode,
doc='''Virtualisation mode: full virtualisation ("HVM"),
or paravirtualisation ("PV"), or hybrid ("PVH")''')
or paravirtualisation ("PV"), or hybrid ("PVH"). TemplateBasedVMs use its '
'template\'s value by default.''')
installed_by_rpm = qubes.property('installed_by_rpm',
type=bool, setter=qubes.property.bool,
@ -397,17 +416,19 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
memory = qubes.property('memory', type=int,
setter=_setter_positive_int,
default=(lambda self:
default=_default_with_template('memory', lambda self:
qubes.config.defaults[
'hvm_memory' if self.virt_mode == 'hvm' else 'memory']),
doc='Memory currently available for this VM.')
doc='Memory currently available for this VM. TemplateBasedVMs use its '
'template\'s value by default.')
maxmem = qubes.property('maxmem', type=int,
setter=_setter_positive_int,
default=(lambda self:
int(min(self.app.host.memory_total / 1024 / 2, 4000))),
default=_default_with_template('maxmem', (lambda self:
int(min(self.app.host.memory_total / 1024 / 2, 4000)))),
doc='''Maximum amount of memory available for this VM (for the purpose
of the memory balancer).''')
of the memory balancer). TemplateBasedVMs use its '
'template\'s value by default.''')
stubdom_mem = qubes.property('stubdom_mem', type=int,
setter=_setter_positive_int,
@ -417,14 +438,17 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
vcpus = qubes.property('vcpus',
type=int,
setter=_setter_positive_int,
default=2,
doc='Number of virtual CPUs for a qube')
default=_default_with_template('vcpus', 2),
doc='Number of virtual CPUs for a qube. TemplateBasedVMs use its '
'template\'s value by default.')
# CORE2: swallowed uses_default_kernel
kernel = qubes.property('kernel', type=str,
setter=_setter_kernel,
default=(lambda self: self.app.default_kernel),
doc='Kernel used by this domain.')
default=_default_with_template('kernel',
lambda self: self.app.default_kernel),
doc='Kernel used by this domain. TemplateBasedVMs use its '
'template\'s value by default.')
# CORE2: swallowed uses_default_kernelopts
# pylint: disable=no-member
@ -434,7 +458,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
if list(self.devices['pci'].persistent())
else self.template.kernelopts if hasattr(self, 'template')
else qubes.config.defaults['kernelopts']),
doc='Kernel command line passed to domain.')
doc='Kernel command line passed to domain. TemplateBasedVMs use its '
'template\'s value by default.')
debug = qubes.property('debug', type=bool, default=False,
setter=qubes.property.bool,
@ -445,10 +470,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
# only plain property?
default_user = qubes.property('default_user', type=str,
# pylint: disable=no-member
default=(lambda self: self.template.default_user
if hasattr(self, 'template') else 'user'),
default=_default_with_template('default_user', 'user'),
setter=_setter_default_user,
doc='FIXME')
doc='Default user to start applications as. TemplateBasedVMs use its '
'template\'s value by default.')
# pylint: enable=no-member
@ -459,7 +484,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
# else:
# return self._default_user
qrexec_timeout = qubes.property('qrexec_timeout', type=int, default=60,
qrexec_timeout = qubes.property('qrexec_timeout', type=int,
default=_default_with_template('qrexec_timeout', 60),
setter=_setter_positive_int,
doc='''Time in seconds after which qrexec connection attempt is deemed
failed. Operating system inside VM should be able to boot in this