From 04fd2ff34a28c073102f5a24afacdb87805066ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sat, 3 Jun 2017 12:22:36 +0200 Subject: [PATCH] vm: adjust VM's root volume when changing its template Re-init volume config of all 'snap_on_start' volumes at template chanage. For this, save original volume config and re-use config_volume_from_source function introduced in previous commit. At the same time, forbid changing template of running AppVM or any DispVM. QubesOS/qubes-issues#2256 --- qubes/vm/appvm.py | 38 ++++++++++++++++++++++++++++++++------ qubes/vm/dispvm.py | 8 ++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/qubes/vm/appvm.py b/qubes/vm/appvm.py index 5f9b220c..0fd0b88c 100644 --- a/qubes/vm/appvm.py +++ b/qubes/vm/appvm.py @@ -39,11 +39,9 @@ class AppVM(qubes.vm.qubesvm.QubesVM): dispvm_allowed = qubes.property('dispvm_allowed', type=bool, default=False, - doc='Should this VM be allowed to start as Disposable VM' - ) + doc='Should this VM be allowed to start as Disposable VM') - def __init__(self, app, xml, template=None, **kwargs): - self.volume_config = { + default_volume_config = { 'root': { 'name': 'root', 'pool': 'default', @@ -78,6 +76,10 @@ class AppVM(qubes.vm.qubesvm.QubesVM): } } + def __init__(self, app, xml, **kwargs): + self.volume_config = copy.deepcopy(self.default_volume_config) + template = kwargs.get('template', None) + if template is not None: # template is only passed if the AppVM is created, in other cases we # don't need to patch the volume_config because the config is @@ -97,8 +99,6 @@ class AppVM(qubes.vm.qubesvm.QubesVM): del self.volume_config[name]['vid'] super(AppVM, self).__init__(app, xml, **kwargs) - if not hasattr(self, 'template') and template is not None: - self.template = template if 'source' not in self.volume_config['root']: msg = 'missing source for root volume' raise qubes.exc.QubesException(msg) @@ -108,3 +108,29 @@ class AppVM(qubes.vm.qubesvm.QubesVM): ''' When domain is loaded assert that this vm has a template. ''' # pylint: disable=unused-argument assert self.template + + @qubes.events.handler('property-pre-set:template') + def on_property_pre_set_template(self, event, name, newvalue, + oldvalue=None): + '''Forbid changing template of running VM + ''' # pylint: disable=unused-argument + if not self.is_halted(): + raise qubes.exc.QubesVMNotHaltedError(self, + 'Cannot change template while qube is running') + + @qubes.events.handler('property-set:template') + def on_property_set_template(self, event, name, newvalue, oldvalue=None): + ''' Adjust root (and possibly other snap_on_start=True) volume + on template change. + ''' # pylint: disable=unused-argument + + for volume_name, conf in self.default_volume_config.items(): + if conf.get('snap_on_start', False) and \ + conf.get('source', None) is None: + config = copy.deepcopy(conf) + template_volume = newvalue.volumes[volume_name] + self.volume_config[volume_name] = \ + self.config_volume_from_source( + config, + template_volume) + self.storage.init_volume(volume_name, config) diff --git a/qubes/vm/dispvm.py b/qubes/vm/dispvm.py index 6ae47225..f83630ef 100644 --- a/qubes/vm/dispvm.py +++ b/qubes/vm/dispvm.py @@ -106,6 +106,14 @@ class DispVM(qubes.vm.qubesvm.QubesVM): ''' # pylint: disable=unused-argument assert self.template + @qubes.events.handler('property-pre-set:template') + def on_property_pre_set_template(self, event, name, newvalue, + oldvalue=None): + ''' Disposable VM cannot have template changed ''' + # pylint: disable=unused-argument + raise qubes.exc.QubesValueError(self, + 'Cannot change template of Disposable VM') + @classmethod def from_appvm(cls, appvm, **kwargs): '''Create a new instance from given AppVM