From b830cb55443262b24a115ad7f510551236ee947a Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Tue, 21 Jun 2016 12:39:47 +0200 Subject: [PATCH 1/9] Volume add eq, neq & hash methods --- qubes/storage/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 64d97329..ebb78468 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -87,7 +87,17 @@ class Volume(object): the libvirt XML template as . ''' return qubes.devices.BlockDevice(self.path, self.name, self.script, - self.rw, self.domain, self.devtype) + self.rw, self.domain, self.devtype) # NOQA + + def __eq__(self, other): + return other.pool == self.pool and other.vid == self.vid \ + and other.volume_type == self.volume_type + + def __neq__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash('%s:%s %s' % (self.pool, self.vid, self.volume_type)) class Storage(object): From 20282c17fe6d80c2d11b6208443458fbf5013de0 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Tue, 21 Jun 2016 12:40:03 +0200 Subject: [PATCH 2/9] Volume add docstrings --- qubes/storage/__init__.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index ebb78468..92209d91 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -30,18 +30,18 @@ from __future__ import absolute_import import os import os.path -import pkg_resources import lxml.etree - +import pkg_resources import qubes +import qubes.devices import qubes.exc import qubes.utils -import qubes.devices STORAGE_ENTRY_POINT = 'qubes.storage' class StoragePoolException(qubes.exc.QubesException): + ''' A general storage exception ''' pass @@ -87,7 +87,7 @@ class Volume(object): the libvirt XML template as . ''' return qubes.devices.BlockDevice(self.path, self.name, self.script, - self.rw, self.domain, self.devtype) # NOQA + self.rw, self.domain, self.devtype) def __eq__(self, other): return other.pool == self.pool and other.vid == self.vid \ @@ -144,7 +144,7 @@ class Storage(object): self.get_pool(volume).resize(volume, size) def create(self, source_template=None): - + ''' Creates volumes on disk ''' if source_template is None and hasattr(self.vm, 'template'): source_template = self.vm.template @@ -159,6 +159,7 @@ class Storage(object): os.umask(old_umask) def clone(self, src_vm): + ''' Clone volumes from the specified vm ''' self.vm.log.info('Creating directory: {0}'.format(self.vm.dir_path)) if not os.path.exists(self.vm.dir_path): self.log.info('Creating directory: {0}'.format(self.vm.dir_path)) @@ -216,6 +217,7 @@ class Storage(object): return self.pools[volume.name] def commit_template_changes(self): + ''' Makes changes to an 'origin' volume persistent ''' for volume in self.vm.volumes.values(): if volume.volume_type == 'origin': self.get_pool(volume).commit_template_changes(volume) @@ -264,6 +266,9 @@ class Pool(object): self.name) def destroy(self): + ''' Called when removing the pool. Use this for implementation specific + clean up. + ''' raise NotImplementedError("Pool %s has destroy() not implemented" % self.name) @@ -283,6 +288,9 @@ class Pool(object): self.name) def setup(self): + ''' Called when adding a pool to the system. Use this for implementation + specific set up. + ''' raise NotImplementedError("Pool %s has setup() not implemented" % self.name) From 72df863bb9ba9b9a2a1d4a1c72bc749dd472c842 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Tue, 21 Jun 2016 12:47:47 +0200 Subject: [PATCH 3/9] Fix qubes.storage.file pylint warnings --- qubes/storage/file.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/qubes/storage/file.py b/qubes/storage/file.py index 5b5e04db..24b63a59 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -86,7 +86,7 @@ class FilePool(Pool): ''' Expands volume, throws :py:class:`qubst.storage.StoragePoolException` if given size is less than current_size - ''' + ''' # pylint: disable=no-self-use _type = volume.volume_type if _type not in ['origin', 'read-write', 'volatile']: raise StoragePoolException('Can not resize a %s volume %s' % @@ -170,7 +170,7 @@ class FilePool(Pool): def start(self, volume): if volume.volume_type == 'volatile': - self._reset_volume(volume) + _reset_volume(volume) if volume.volume_type in ['origin', 'snapshot']: _check_path(volume.path_origin) _check_path(volume.path_cow) @@ -182,18 +182,6 @@ class FilePool(Pool): def stop(self, volume): pass - @staticmethod - def _reset_volume(volume): - ''' Remove and recreate a volatile volume ''' - assert volume.volume_type == 'volatile', "Not a volatile volume" - assert volume.size - - _remove_if_exists(volume.path) - - with open(volume.path, "w") as f_volatile: - f_volatile.truncate(volume.size) - return volume - def target_dir(self, vm): """ Returns the path to vmdir depending on the type of the VM. @@ -251,6 +239,7 @@ class FilePool(Pool): expected_origin_type origin_pool = vm.app.get_pool(origin_vm.volume_config[name]['pool']) + assert isinstance(origin_pool, FilePool), 'Origin volume not a file volume' @@ -513,7 +502,7 @@ def copy_file(source, destination): def _remove_if_exists(path): - ''' Removes a path if it exist, silently succeeds if file does not exist ''' + ''' Removes a file if it exist, silently succeeds if file does not exist ''' if os.path.exists(path): os.remove(path) @@ -522,3 +511,16 @@ def _check_path(path): ''' Raise an StoragePoolException if ``path`` does not exist''' if not os.path.exists(path): raise StoragePoolException('Missing image file: %s' % path) + + +def _reset_volume(volume): + ''' Remove and recreate a volatile volume ''' + assert volume.volume_type == 'volatile', "Not a volatile volume" + + assert volume.size + + _remove_if_exists(volume) + + with open(volume.path, "w") as f_volatile: + f_volatile.truncate(volume.size) + return volume From 803efa76ff5f46e80d421d842ebc10fbd090f63e Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Tue, 21 Jun 2016 12:48:57 +0200 Subject: [PATCH 4/9] Merge FilePool._resize_loop_device() with resize() --- qubes/storage/file.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/qubes/storage/file.py b/qubes/storage/file.py index 24b63a59..2d9254d3 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -107,7 +107,18 @@ class FilePool(Pool): with open(path, 'a+b') as fd: fd.truncate(size) - self._resize_loop_device(path) + p = subprocess.Popen( + ['sudo', 'losetup', '--associated', path], + stdout=subprocess.PIPE) + result = p.communicate() + + m = re.match(r'^(/dev/loop\d+):\s', result[0]) + if m is not None: + loop_dev = m.group(1) + + # resize loop device + subprocess.check_call(['sudo', 'losetup', '--set-capacity', loop_dev + ]) def remove(self, volume): if volume.volume_type in ['read-write', 'volatile']: @@ -128,23 +139,6 @@ class FilePool(Pool): return volume - @staticmethod - def _resize_loop_device(path): - ''' Sets the loop device capacity ''' - # find loop device if any - p = subprocess.Popen( - ['sudo', 'losetup', '--associated', path], - stdout=subprocess.PIPE) - result = p.communicate() - - m = re.match(r'^(/dev/loop\d+):\s', result[0]) - if m is not None: - loop_dev = m.group(1) - - # resize loop device - subprocess.check_call(['sudo', 'losetup', '--set-capacity', - loop_dev]) - def commit_template_changes(self, volume): if volume.volume_type != 'origin': return volume From db608f6e015a68dec602a333b3656227e16d1906 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Sun, 5 Jun 2016 20:18:56 +0200 Subject: [PATCH 5/9] Pool add str, eq & neq --- qubes/storage/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 92209d91..2d070f99 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -233,11 +233,21 @@ class Pool(object): private_img_size = qubes.config.defaults['private_img_size'] root_img_size = qubes.config.defaults['root_img_size'] + def __eq__(self, other): + return self.name == other.name + + + def __neq__(self, other): + return not self.__eq__(other) + def __init__(self, name, **kwargs): super(Pool, self).__init__(**kwargs) self.name = name kwargs['name'] = self.name + def __str__(self): + return self.name + def __xml__(self): return lxml.etree.Element('pool', **self.config) From 3dd77719c116cfe708db199eef3e38d216a42c7c Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Sun, 29 May 2016 00:51:06 +0200 Subject: [PATCH 6/9] Pool.create source_volume argument is optional --- qubes/storage/__init__.py | 2 +- qubes/storage/kernels.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 2d070f99..d82fe6ec 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -251,7 +251,7 @@ class Pool(object): def __xml__(self): return lxml.etree.Element('pool', **self.config) - def create(self, volume, source_volume): + def create(self, volume, source_volume=None): ''' Create the given volume on disk or copy from provided `source_volume`. ''' diff --git a/qubes/storage/kernels.py b/qubes/storage/kernels.py index f524849c..31477d88 100644 --- a/qubes/storage/kernels.py +++ b/qubes/storage/kernels.py @@ -67,7 +67,7 @@ class LinuxKernel(Pool): def clone(self, source, target): return target - def create(self, volume, source_volume): + def create(self, volume, source_volume=None): return volume def commit_template_changes(self, volume): From 8fc3772017d528c0caac0f49e5b6b4ce1725c588 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Sat, 28 May 2016 18:06:12 +0200 Subject: [PATCH 7/9] Add Volume.__str__() --- qubes/storage/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index d82fe6ec..e07d772d 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -99,6 +99,9 @@ class Volume(object): def __hash__(self): return hash('%s:%s %s' % (self.pool, self.vid, self.volume_type)) + def __str__(self): + return "{!s}:{!s}".format(self.pool, self.vid) + class Storage(object): ''' Class for handling VM virtual disks. From 12745a4860134ce5b30fb17c454044a6f6486036 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Sat, 28 May 2016 21:07:29 +0200 Subject: [PATCH 8/9] Qubes.add_pool() returns the added pool --- qubes/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubes/app.py b/qubes/app.py index 30570e6e..805a64f4 100644 --- a/qubes/app.py +++ b/qubes/app.py @@ -911,6 +911,7 @@ class Qubes(qubes.PropertyHolder): pool = self._get_pool(**kwargs) pool.setup() self.pools[name] = pool + return pool def remove_pool(self, name): """ Remove a storage pool from config file. """ From 4db84c42a6cd69136dfdf0d77a697638e83e3036 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Tue, 21 Jun 2016 16:30:37 +0200 Subject: [PATCH 9/9] Fix qubes.storage.file _remove_if_exists --- qubes/storage/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubes/storage/file.py b/qubes/storage/file.py index 2d9254d3..ab0e0a6b 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -513,7 +513,7 @@ def _reset_volume(volume): assert volume.size - _remove_if_exists(volume) + _remove_if_exists(volume.path) with open(volume.path, "w") as f_volatile: f_volatile.truncate(volume.size)