Use entry_points for pool driver discovery
- Add qubes.storage entry point to setup.py - Removed the old pool driver class loading logic - Reworked pool tests
This commit is contained in:
parent
42666e0ec5
commit
b1978abce5
@ -2,10 +2,11 @@
|
||||
driver=xen ; the default xen storage
|
||||
; class = qubes.storage.xen.XenStorage ; class always overwrites the driver
|
||||
;
|
||||
; To use our own storage adapter, you need just to specify the module path and
|
||||
; To use our own pool driver it needs to provide `qubes.storage` entry_point
|
||||
; see also: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
|
||||
; class name
|
||||
; [pool-b]
|
||||
; class = foo.bar.MyStorage
|
||||
; driver = foo
|
||||
;
|
||||
; [test-dummy]
|
||||
; driver=dummy
|
||||
|
@ -83,8 +83,6 @@ defaults = {
|
||||
'private_img_size': 2*1024*1024*1024,
|
||||
'root_img_size': 10*1024*1024*1024,
|
||||
|
||||
'storage_class': 'qubes.storage.xen.XenStorage',
|
||||
'pool_drivers': {'xen': 'qubes.storage.xen.XenPool'},
|
||||
'pool_config': {'dir_path': '/var/lib/qubes'},
|
||||
|
||||
# how long (in sec) to wait for VMs to shutdown,
|
||||
|
@ -24,23 +24,24 @@
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
""" Qubes storage system"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import ConfigParser
|
||||
import importlib
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pkg_resources
|
||||
import qubes
|
||||
import qubes.exc
|
||||
import qubes.utils
|
||||
|
||||
BLKSIZE = 512
|
||||
CONFIG_FILE = '/etc/qubes/storage.conf'
|
||||
STORAGE_ENTRY_POINT = 'qubes.storage'
|
||||
|
||||
|
||||
class StoragePoolException(qubes.exc.QubesException):
|
||||
@ -78,7 +79,6 @@ class Storage(object):
|
||||
#: Additional drive (currently used only by HVM)
|
||||
self.drive = None
|
||||
|
||||
|
||||
def get_config_params(self):
|
||||
args = {}
|
||||
args['rootdev'] = self.root_dev_config()
|
||||
@ -90,7 +90,6 @@ class Storage(object):
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def root_dev_config(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -357,42 +356,6 @@ def get_disk_usage(path):
|
||||
return ret
|
||||
|
||||
|
||||
def load(clsname):
|
||||
'''Given a dotted full module string representation of a class it loads it
|
||||
|
||||
Args:
|
||||
string (str) i.e. 'qubes.storage.xen.QubesXenVmStorage'
|
||||
|
||||
Returns:
|
||||
type
|
||||
|
||||
See also:
|
||||
:func:`qubes.storage.dump`
|
||||
|
||||
:raises ImportError: when storage class specified in config cannot be found
|
||||
:raises KeyError: when storage class specified in config cannot be found
|
||||
'''
|
||||
|
||||
if not isinstance(clsname, basestring):
|
||||
return clsname
|
||||
pkg, cls = clsname.strip().rsplit('.', 1)
|
||||
|
||||
# this may raise ImportError or KeyError, that's okay
|
||||
return importlib.import_module(pkg).__dict__[cls]
|
||||
|
||||
|
||||
def dump(o):
|
||||
""" Returns a string represention of the given object
|
||||
|
||||
Args:
|
||||
o (object): anything that response to `__module__` and `__class__`
|
||||
|
||||
Given the class :class:`qubes.storage.QubesVmStorage` it returns
|
||||
'qubes.storage.QubesVmStorage' as string
|
||||
"""
|
||||
return o.__module__ + '.' + o.__class__.__name__
|
||||
|
||||
|
||||
def get_pool(name, vm):
|
||||
""" Instantiates the storage for the specified vm """
|
||||
config = _get_storage_config_parser()
|
||||
@ -465,16 +428,15 @@ def _get_pool_klass(name, config=None):
|
||||
|
||||
if not config.has_section(name):
|
||||
raise StoragePoolException('Uknown storage pool ' + name)
|
||||
elif not config.has_option(name, 'driver'):
|
||||
raise StoragePoolException('No driver specified for pool ' + name)
|
||||
|
||||
if config.has_option(name, 'class'):
|
||||
klass = load(config.get(name, 'class'))
|
||||
elif config.has_option(name, 'driver'):
|
||||
pool_driver = config.get(name, 'driver')
|
||||
klass = load(qubes.config.defaults['pool_drivers'][pool_driver])
|
||||
else:
|
||||
raise StoragePoolException('Uknown storage pool driver ' + name)
|
||||
return klass
|
||||
|
||||
driver = config.get(name, 'driver')
|
||||
try:
|
||||
return qubes.get_entry_point_one(STORAGE_ENTRY_POINT, driver)
|
||||
except KeyError:
|
||||
raise StoragePoolException('Driver %s for pool %s' % (driver, name))
|
||||
|
||||
class Pool(object):
|
||||
def __init__(self, vm, dir_path):
|
||||
@ -533,3 +495,9 @@ class Pool(object):
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
|
||||
|
||||
def pool_drivers():
|
||||
""" Return a list of EntryPoints names """
|
||||
return [ep.name
|
||||
for ep in pkg_resources.iter_entry_points(STORAGE_ENTRY_POINT)]
|
||||
|
@ -16,16 +16,16 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
import qubes.storage
|
||||
import qubes.log
|
||||
from qubes.config import defaults
|
||||
from qubes.storage.xen import XenPool, XenStorage
|
||||
from qubes.tests import QubesTestCase, SystemTestsMixin
|
||||
from qubes.storage import StoragePoolException, pool_drivers
|
||||
from qubes.storage.xen import XenPool
|
||||
from qubes.tests import QubesTestCase
|
||||
|
||||
|
||||
class TestApp(qubes.tests.TestEmitter):
|
||||
pass
|
||||
|
||||
|
||||
class TestVM(object):
|
||||
def __init__(self, app, qid, name, pool_name, template=None):
|
||||
super(TestVM, self).__init__()
|
||||
@ -35,8 +35,8 @@ class TestVM(object):
|
||||
self.pool_name = pool_name
|
||||
self.template = template
|
||||
self.hvm = False
|
||||
self.storage = qubes.storage.get_pool(
|
||||
self.pool_name, self).get_storage()
|
||||
self.storage = qubes.storage.get_pool(self.pool_name,
|
||||
self).get_storage()
|
||||
self.log = qubes.log.get_vm_logger(self.name)
|
||||
|
||||
def is_template(self):
|
||||
@ -54,56 +54,44 @@ class TestTemplateVM(TestVM):
|
||||
def is_template(self):
|
||||
return True
|
||||
|
||||
|
||||
class TestDisposableVM(TestVM):
|
||||
def is_disposablevm(self):
|
||||
return True
|
||||
|
||||
class TC_00_Storage(QubesTestCase):
|
||||
|
||||
class TC_00_Pool(QubesTestCase):
|
||||
""" This class tests the utility methods from :mod:``qubes.storage`` """
|
||||
|
||||
def setUp(self):
|
||||
super(TC_00_Storage, self).setUp()
|
||||
self.app = TestApp()
|
||||
super(TC_00_Pool, self).setUp()
|
||||
|
||||
def test_000_dump(self):
|
||||
""" Dumps storage instance to a storage string """
|
||||
vmname = self.make_vm_name('appvm')
|
||||
template = TestTemplateVM(self.app, 1,
|
||||
qubes.tests.VMPREFIX + 'template', pool_name='default')
|
||||
vm = TestVM(self.app, qid=2, name=vmname, pool_name='default',
|
||||
template=template)
|
||||
storage = vm.storage
|
||||
result = qubes.storage.dump(storage)
|
||||
expected = 'qubes.storage.xen.XenStorage'
|
||||
self.assertEquals(result, expected)
|
||||
def test_000_unknown_pool_driver(self):
|
||||
# :pylint: disable=protected-access
|
||||
""" Expect an exception when unknown pool is requested"""
|
||||
with self.assertRaises(StoragePoolException):
|
||||
qubes.storage._get_pool_klass('foo-bar')
|
||||
|
||||
def test_001_load(self):
|
||||
""" Loads storage driver from a storage string """
|
||||
result = qubes.storage.load('qubes.storage.xen.XenStorage')
|
||||
self.assertTrue(result is XenStorage)
|
||||
def test_001_all_pool_drivers(self):
|
||||
""" The only predefined pool driver is file """
|
||||
self.assertEquals(["xen"], pool_drivers())
|
||||
|
||||
def test_002_default_pool_drivers(self):
|
||||
""" The only predifined pool driver is xen """
|
||||
result = defaults['pool_drivers'].keys()
|
||||
expected = ["xen"]
|
||||
self.assertEquals(result, expected)
|
||||
|
||||
def test_003_get_pool_klass(self):
|
||||
def test_002_get_pool_klass(self):
|
||||
""" Expect the default pool to be `XenPool` """
|
||||
# :pylint: disable=protected-access
|
||||
result = qubes.storage._get_pool_klass('default')
|
||||
self.assertTrue(result is XenPool)
|
||||
|
||||
def test_004_pool_exists_default(self):
|
||||
def test_003_pool_exists_default(self):
|
||||
""" Expect the default pool to exists """
|
||||
self.assertTrue(qubes.storage.pool_exists('default'))
|
||||
|
||||
def test_005_pool_exists_random(self):
|
||||
def test_004_pool_exists_random(self):
|
||||
""" Expect this pool to not a exist """
|
||||
self.assertFalse(
|
||||
qubes.storage.pool_exists('asdh312096r832598213iudhas'))
|
||||
self.assertFalse(qubes.storage.pool_exists(
|
||||
'asdh312096r832598213iudhas'))
|
||||
|
||||
def test_006_add_remove_pool(self):
|
||||
def test_005_add_remove_pool(self):
|
||||
""" Tries to adding and removing a pool. """
|
||||
pool_name = 'asdjhrp89132'
|
||||
|
||||
|
12
setup.py
12
setup.py
@ -1,10 +1,11 @@
|
||||
#!/usr/bin/python2 -O
|
||||
# vim: fileencoding=utf-8
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
# don't import: import * is unreliable and there is no need, since this is
|
||||
# compile time and we have source files
|
||||
def get_console_scripts():
|
||||
@ -15,6 +16,7 @@ def get_console_scripts():
|
||||
yield '{} = qubes.tools.{}:main'.format(
|
||||
basename.replace('_', '-'), basename)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
setuptools.setup(
|
||||
name='qubes',
|
||||
@ -24,9 +26,7 @@ if __name__ == '__main__':
|
||||
description='Qubes core package',
|
||||
license='GPL2+',
|
||||
url='https://www.qubes-os.org/',
|
||||
|
||||
packages=setuptools.find_packages(exclude=('core*', 'tests')),
|
||||
|
||||
entry_points={
|
||||
'console_scripts': list(get_console_scripts()),
|
||||
'qubes.vm': [
|
||||
@ -42,5 +42,7 @@ if __name__ == '__main__':
|
||||
'qubes.devices': [
|
||||
'pci = qubes.devices:PCIDevice',
|
||||
],
|
||||
}
|
||||
)
|
||||
'qubes.storage': [
|
||||
'xen = qubes.storage.xen:XenPool',
|
||||
]
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user