Add pool config parsing

This commit is contained in:
Bahtiar `kalkin-` Gadimov 2015-11-07 19:01:25 +01:00
parent 15d5e6edbb
commit bfaf37dae5
7 changed files with 176 additions and 0 deletions

View File

@ -1,5 +1,6 @@
OS ?= Linux OS ?= Linux
SYSCONFDIR ?= /etc
PYTHON_QUBESPATH = $(PYTHON_SITEPATH)/qubes PYTHON_QUBESPATH = $(PYTHON_SITEPATH)/qubes
all: all:
@ -13,6 +14,8 @@ endif
mkdir -p $(DESTDIR)$(PYTHON_QUBESPATH)/storage mkdir -p $(DESTDIR)$(PYTHON_QUBESPATH)/storage
cp __init__.py $(DESTDIR)$(PYTHON_QUBESPATH)/storage cp __init__.py $(DESTDIR)$(PYTHON_QUBESPATH)/storage
cp __init__.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage cp __init__.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage
mkdir -p $(DESTDIR)$(SYSCONFDIR)/qubes
cp storage.conf $(DESTDIR)$(SYSCONFDIR)/qubes/
ifneq ($(BACKEND_VMM),) ifneq ($(BACKEND_VMM),)
if [ -r $(BACKEND_VMM).py ]; then \ if [ -r $(BACKEND_VMM).py ]; then \
cp $(BACKEND_VMM).py $(DESTDIR)$(PYTHON_QUBESPATH)/storage && \ cp $(BACKEND_VMM).py $(DESTDIR)$(PYTHON_QUBESPATH)/storage && \

View File

@ -22,6 +22,7 @@
from __future__ import absolute_import from __future__ import absolute_import
import ConfigParser
import os import os
import os.path import os.path
import shutil import shutil
@ -31,6 +32,8 @@ import sys
import qubes.qubesutils import qubes.qubesutils
from qubes.qubes import QubesException, defaults, system_path, vm_files from qubes.qubes import QubesException, defaults, system_path, vm_files
CONFIG_FILE = '/etc/qubes/storage.conf'
class QubesVmStorage(object): class QubesVmStorage(object):
""" """
@ -200,5 +203,97 @@ class QubesVmStorage(object):
self.create_on_disk_private_img(verbose=False) self.create_on_disk_private_img(verbose=False)
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 load(string):
""" 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`
"""
if not type(string) is str:
# This is a hack which allows giving a real class to a vm instead of a
# string as string_class parameter.
return string
components = string.split(".")
module_path = ".".join(components[:-1])
klass = components[-1:][0]
module = __import__(module_path, fromlist=[klass])
return getattr(module, klass)
def get_pool(vm):
""" Instantiates the storage for the specified vm """
config = _get_storage_config_parser()
name = vm.storage_pool
klass = _get_pool_klass(name, config)
keys = [k for k in config.options(name) if k != 'type' and k != 'class']
values = [config.get(name, o) for o in keys]
kwargs = dict(zip(keys, values))
return klass(vm, **kwargs)
def _get_storage_config_parser():
""" Instantiates a `ConfigParaser` for specified storage config file.
Returns:
RawConfigParser
"""
config = ConfigParser.RawConfigParser()
config.read(CONFIG_FILE)
return config
def _get_pool_klass(name, config=None):
""" Returns the storage klass for the specified pool.
Args:
name: The pool name.
config: If ``config`` is not specified
`_get_storage_config_parser()` is called.
Returns:
type: A class inheriting from `QubesVmStorage`
"""
if config is None:
config = _get_storage_config_parser()
if not config.has_section(name):
raise StoragePoolException('Uknown storage pool ' + name)
if config.has_option(name, 'class'):
klass = load(config.get(name, 'class'))
elif config.has_option(name, 'type'):
pool_type = config.get(name, 'type')
klass = defaults['pool_types'][pool_type]
if klass is None:
raise StoragePoolException('Uknown storage pool type ' + name)
return klass
class StoragePoolException(QubesException):
pass
class Pool(object): class Pool(object):
pass pass

11
core/storage/storage.conf Normal file
View File

@ -0,0 +1,11 @@
[default] ; poolname
type=xen ; the default xen storage
; class = qubes.storage.xen.XenStorage ; class always overwrites type
;
; To use our own storage adapter, you need just to specify the module path and
; class name
; [pool-b]
; class = foo.bar.MyStorage
;

View File

@ -176,6 +176,7 @@ fi
%files %files
%defattr(-,root,root,-) %defattr(-,root,root,-)
%config(noreplace) %attr(0664,root,qubes) %{_sysconfdir}/qubes/qmemman.conf %config(noreplace) %attr(0664,root,qubes) %{_sysconfdir}/qubes/qmemman.conf
%config(noreplace) %attr(0664,root,qubes) %{_sysconfdir}/qubes/storage.conf
/usr/bin/qvm-* /usr/bin/qvm-*
/usr/bin/qubes-* /usr/bin/qubes-*
%dir %{python_sitearch}/qubes %dir %{python_sitearch}/qubes

View File

@ -27,3 +27,5 @@ endif
cp regressions.py[co] $(DESTDIR)$(PYTHON_TESTSPATH) cp regressions.py[co] $(DESTDIR)$(PYTHON_TESTSPATH)
cp run.py $(DESTDIR)$(PYTHON_TESTSPATH) cp run.py $(DESTDIR)$(PYTHON_TESTSPATH)
cp run.py[co] $(DESTDIR)$(PYTHON_TESTSPATH) cp run.py[co] $(DESTDIR)$(PYTHON_TESTSPATH)
cp storage.py $(DESTDIR)$(PYTHON_TESTSPATH)
cp storage.py[co] $(DESTDIR)$(PYTHON_TESTSPATH)

View File

@ -547,6 +547,7 @@ def load_tests(loader, tests, pattern):
'qubes.tests.backup', 'qubes.tests.backup',
'qubes.tests.backupcompatibility', 'qubes.tests.backupcompatibility',
'qubes.tests.regressions', 'qubes.tests.regressions',
'qubes.tests.storage',
): ):
tests.addTests(loader.loadTestsFromName(modname)) tests.addTests(loader.loadTestsFromName(modname))

63
tests/storage.py Normal file
View File

@ -0,0 +1,63 @@
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from qubes.tests import QubesTestCase, SystemTestsMixin
from qubes.qubes import defaults
import qubes.storage
from qubes.storage.xen import QubesXenVmStorage, XenPool
class TC_00_Storage(SystemTestsMixin, QubesTestCase):
def test_000_dump(self):
""" Dumps storage instance to a storage string """
vmname = self.make_vm_name('appvm')
template = self.qc.get_default_template()
storage = self.qc.add_new_vm('QubesAppVm', name=vmname, pool='default',
template=template).storage
result = qubes.storage.dump(storage)
expected = 'qubes.storage.xen.QubesXenVmStorage'
self.assertEquals(result, expected)
def test_001_load(self):
""" Loads storage type from a storage string """
result = qubes.storage.load('qubes.storage.xen.QubesXenVmStorage')
self.assertTrue(result is QubesXenVmStorage)
def test_002_default_pool_types(self):
""" The only predifined pool type is xen """
result = defaults['pool_types'].keys()
expected = ["xen"]
self.assertEquals(result, expected)
def test_003_get_pool_klass(self):
""" Expect the default pool to be `XenPool` """
result = qubes.storage._get_pool_klass('default')
self.assertTrue(result is XenPool)
class TC_01_Storage(SystemTestsMixin, QubesTestCase):
def test_000_vm_use_default_pool(self):
vmname = self.make_vm_name('appvm')
template = self.qc.get_default_template()
vm = self.qc.add_new_vm('QubesAppVm', name=vmname, template=template,
pool='default')
self.assertIsInstance(vm.storage, QubesXenVmStorage)