diff --git a/qubes/storage/xen.py b/qubes/storage/xen.py index 097bc705..49a03be9 100644 --- a/qubes/storage/xen.py +++ b/qubes/storage/xen.py @@ -33,41 +33,32 @@ import subprocess import qubes import qubes.config -import qubes.storage import qubes.vm.templatevm -from qubes.devices import BlockDevice +from qubes.storage import Pool, StoragePoolException, Volume -class XenStorage(qubes.storage.Storage): - '''Class for VM storage of Xen VMs. - ''' +class XenPool(Pool): root_dev = 'xvda' private_dev = 'xvdb' volatile_dev = 'xvdc' - modules_dev = 'xvdd' - def __init__(self, vm, vmdir, **kwargs): - """ Instantiate the storage. - - Args: - vm: a QubesVM - vmdir: the root directory of the pool - """ - assert vm is not None - assert vmdir is not None - - super(XenStorage, self).__init__(vm, **kwargs) - - self.vmdir = vmdir + def __init__(self, vm=None, name=None, dir_path=None): + super(XenPool, self).__init__(vm=vm, name=name) + assert dir_path, "No pool dir_path specified" + self.dir_path = os.path.normpath(dir_path) + self.create_dir_if_not_exists(self.dir_path) + appvms_path = os.path.join(self.dir_path, 'appvms') + self.create_dir_if_not_exists(appvms_path) + vm_templates_path = os.path.join(self.dir_path, 'vm-templates') + self.create_dir_if_not_exists(vm_templates_path) @property def private_img(self): '''Path to the private image''' return self.abspath(qubes.config.vm_files['private_img']) - @property def root_img(self): '''Path to the root image''' @@ -75,7 +66,6 @@ class XenStorage(qubes.storage.Storage): if hasattr(self.vm, 'template') and self.vm.template \ else self.abspath(qubes.config.vm_files['root_img']) - @property def rootcow_img(self): '''Path to the root COW image''' @@ -85,17 +75,11 @@ class XenStorage(qubes.storage.Storage): return None - @property def volatile_img(self): '''Path to the volatile image''' return self.abspath(qubes.config.vm_files['volatile_img']) - def format_disk_dev(self, path, name, script=None, rw=True, devtype='disk', - domain=None): - - return BlockDevice(path, name, script, rw, domain, devtype) - def root_dev_config(self): dev_name = 'root' if isinstance(self.vm, qubes.vm.templatevm.TemplateVM): @@ -123,27 +107,27 @@ class XenStorage(qubes.storage.Storage): # any other template-based VM - two device-mapper layers: one # in dom0 (here) from root+root-cow, and another one from # this+volatile.img - return self.format_disk_dev( - '{root}:{template_rootcow}'.format( - root=self.root_img, - template_rootcow=self.vm.template.storage.rootcow_img), - self.root_dev, - script='block-snapshot', - rw=False) + path = '{root}:{template_rootcow}'.format( + root=self.root_img, + template_rootcow=self.vm.template.storage.rootcow_img) + return self.format_disk_dev(path=path, + vdev=self.root_dev, + script='block-snapshot', + rw=False) else: # standalone qube return self.format_disk_dev(self.root_img, dev_name) - def private_dev_config(self): return self.format_disk_dev(self.private_img, 'private') def volatile_dev_config(self): return self.format_disk_dev(self.volatile_img, 'volatile') - def create_on_disk_private_img(self, source_template=None): + if not os.path.exists(self.target_dir): + os.makedirs(self.target_dir) if source_template is None: f_private = open(self.private_img, 'a+b') f_private.truncate(self.private_img_size) @@ -152,11 +136,11 @@ class XenStorage(qubes.storage.Storage): else: self.vm.log.info("Copying the template's private image: {}".format( source_template.storage.private_img)) - self._copy_file(source_template.storage.private_img, - self.private_img) - + self._copy_file(source_template.storage.private_img, self.private_img) def create_on_disk_root_img(self, source_template=None): + if not os.path.exists(self.target_dir): + os.makedirs(self.target_dir) if source_template is None: fd = open(self.root_img, 'a+b') fd.truncate(self.root_img_size) @@ -164,11 +148,11 @@ class XenStorage(qubes.storage.Storage): elif self.vm.updateable: # if not updateable, just use template's disk - self.vm.log.info("--> Copying the template's root image: {}".format( - source_template.storage.root_img)) + self.vm.log.info( + "--> Copying the template's root image: {}".format( + source_template.storage.root_img)) self._copy_file(source_template.storage.root_img, self.root_img) - def resize_private_img(self, size): fd = open(self.private_img, 'a+b') fd.truncate(size) @@ -185,9 +169,8 @@ class XenStorage(qubes.storage.Storage): loop_dev = m.group(1) # resize loop device - subprocess.check_call( - ['sudo', 'losetup', '--set-capacity', loop_dev]) - + subprocess.check_call(['sudo', 'losetup', '--set-capacity', + loop_dev]) def commit_template_changes(self): assert isinstance(self.vm, qubes.vm.templatevm.TemplateVM) @@ -206,7 +189,6 @@ class XenStorage(qubes.storage.Storage): f_root.close() os.umask(old_umask) - def reset_volatile_storage(self): try: # no template set, in any way (Standalone VM, Template VM) @@ -241,15 +223,14 @@ class XenStorage(qubes.storage.Storage): f_volatile.truncate(f_root.tell()) f_volatile.close() f_root.close() - return # XXX why is that? super() does not run - except AttributeError: # self.vm.template + return # XXX why is that? super() does not run + except AttributeError: # self.vm.template pass - super(XenStorage, self).reset_volatile_storage() - + super(XenPool, self).reset_volatile_storage() def prepare_for_vm_startup(self): - super(XenStorage, self).prepare_for_vm_startup() + super(XenPool, self).prepare_for_vm_startup() if self.drive is not None: # pylint: disable=unused-variable @@ -260,14 +241,44 @@ class XenStorage(qubes.storage.Storage): drive_vm = self.vm.app.domains[drive_domain] if not drive_vm.is_running(): - raise qubes.exc.QubesVMNotRunningError(drive_vm, - 'VM {!r} holding {!r} isn\'t running'.format( + raise qubes.exc.QubesVMNotRunningError( + drive_vm, 'VM {!r} holding {!r} isn\'t running'.format( drive_domain, drive_path)) if self.rootcow_img and not os.path.exists(self.rootcow_img): self.commit_template_changes() -class XenPool(qubes.storage.Pool): - def get_storage(self): - """ Returns an instantiated ``XenStorage``. """ - return XenStorage(self.vm, vmdir=self.vmdir) + # XXX there is also a class attribute on the domain classes which does + # exactly that -- which one should prevail? + @property + def target_dir(self): + """ Returns the path to vmdir depending on the type of the VM. + + The default QubesOS file storage saves the vm images in three + different directories depending on the ``QubesVM`` type: + + * ``appvms`` for ``QubesAppVm`` or ``QubesHvm`` + * ``vm-templates`` for ``QubesTemplateVm`` or ``QubesTemplateHvm`` + + Args: + vm: a QubesVM + pool_dir: the root directory of the pool + + Returns: + string (str) absolute path to the directory where the vm files + are stored + """ + vm = self.vm + if vm.is_template(): + subdir = 'vm-templates' + elif vm.is_disposablevm(): + subdir = 'appvms' + return os.path.join(self.dir_path, subdir, + vm.template.name + '-dvm') + else: + subdir = 'appvms' + + return os.path.join(self.dir_path, subdir, vm.name) + + def abspath(self, file_name): + return os.path.join(self.target_dir, file_name)