Add pool config parsing
This commit is contained in:
parent
15d5e6edbb
commit
bfaf37dae5
@ -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 && \
|
||||||
|
@ -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
11
core/storage/storage.conf
Normal 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
|
||||||
|
;
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
63
tests/storage.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user