Reverted Storage ←→ Pool dependency

- Storage() operates on a pool and in future on multiple pools
This commit is contained in:
Bahtiar `kalkin-` Gadimov 2016-04-15 20:13:06 +02:00
parent 9d646aabd3
commit 32255a7916
2 changed files with 113 additions and 124 deletions

View File

@ -37,6 +37,7 @@ import pkg_resources
import qubes import qubes
import qubes.exc import qubes.exc
import qubes.utils import qubes.utils
from qubes.devices import BlockDevice
BLKSIZE = 512 BLKSIZE = 512
STORAGE_ENTRY_POINT = 'qubes.storage' STORAGE_ENTRY_POINT = 'qubes.storage'
@ -92,29 +93,37 @@ class Storage(object):
in mind. in mind.
''' '''
root_img = None modules_dev = 'xvdd'
private_img = None
volatile_img = None
modules_dev = None
def __init__(self, vm, private_img_size=None, root_img_size=None):
def __init__(self, vm):
#: Domain for which we manage storage #: Domain for which we manage storage
self.vm = vm self.vm = vm
#: Size of the private image
self.private_img_size = private_img_size \
if private_img_size is not None \
else qubes.config.defaults['private_img_size']
#: Size of the root image
self.root_img_size = root_img_size \
if root_img_size is not None \
else qubes.config.defaults['root_img_size']
#: Additional drive (currently used only by HVM) #: Additional drive (currently used only by HVM)
self.drive = None self.drive = None
for name, conf in self.vm.volume_config.items():
assert 'pool' in conf
pool = get_pool(conf['pool'], self.vm)
self.vm.volumes[name] = pool.init_volume(conf)
@property
def root_img(self):
pool = self.get_pool()
return pool.root_img
@property
def private_img(self):
pool = self.get_pool()
return pool.private_img
@property
def volatile_img(self):
pool = self.get_pool()
return pool.volatile_img
@property
def rootcow_img(self):
pool = self.get_pool()
return pool.rootcow_img
def get_config_params(self): def get_config_params(self):
args = {} args = {}
@ -128,17 +137,25 @@ class Storage(object):
return args return args
def root_dev_config(self): def root_dev_config(self):
raise NotImplementedError() pool = self.get_pool()
return pool.root_dev_config()
def private_dev_config(self): def private_dev_config(self):
raise NotImplementedError() pool = self.get_pool()
return pool.private_dev_config()
def volatile_dev_config(self): def volatile_dev_config(self):
raise NotImplementedError() pool = self.get_pool()
return pool.volatile_dev_config()
def modules_dev_config(self):
return self.format_disk_dev(self.modules_img,
'kernel',
rw=self.modules_img_rw)
def other_dev_config(self): def other_dev_config(self):
if self.modules_img is not None: if self.modules_img is not None:
return BlockDevice(self.modules_img, 'kernel', rw=False) return self.modules_dev_config()
elif self.drive is not None: elif self.drive is not None:
(drive_type, drive_domain, drive_path) = self.drive.split(":") (drive_type, drive_domain, drive_path) = self.drive.split(":")
if drive_type == 'hd': if drive_type == 'hd':
@ -160,7 +177,7 @@ class Storage(object):
def format_disk_dev(self, path, name, script=None, rw=True, devtype='disk', def format_disk_dev(self, path, name, script=None, rw=True, devtype='disk',
domain=None): domain=None):
raise NotImplementedError() return BlockDevice(path, name, script, rw, domain, devtype)
@property @property
def kernels_dir(self): def kernels_dir(self):
@ -175,7 +192,6 @@ class Storage(object):
else os.path.join(self.vm.dir_path, else os.path.join(self.vm.dir_path,
qubes.config.vm_files['kernels_subdir']) qubes.config.vm_files['kernels_subdir'])
@property @property
def modules_img(self): def modules_img(self):
'''Path to image with modules. '''Path to image with modules.
@ -198,29 +214,6 @@ class Storage(object):
return self.vm.kernel is None return self.vm.kernel is None
def abspath(self, path, rel=None):
'''Make absolute path.
If given path is relative, it is interpreted as relative to
:py:attr:`self.vm.dir_path` or given *rel*.
'''
return path if os.path.isabs(path) \
else os.path.join(rel or self.vm.dir_path, path)
@staticmethod
def _copy_file(source, destination):
'''Effective file copy, preserving sparse files etc.
'''
# TODO: Windows support
# We prefer to use Linux's cp, because it nicely handles sparse files
try:
subprocess.check_call(['cp', '--reflink=auto', source, destination])
except subprocess.CalledProcessError:
raise IOError('Error while copying {!r} to {!r}'.format(
source, destination))
def get_disk_utilization(self): def get_disk_utilization(self):
return get_disk_usage(self.vm.dir_path) return get_disk_usage(self.vm.dir_path)
@ -237,12 +230,6 @@ class Storage(object):
def resize_private_img(self, size): def resize_private_img(self, size):
raise NotImplementedError() raise NotImplementedError()
def create_on_disk_private_img(self, source_template=None):
raise NotImplementedError()
def create_on_disk_root_img(self, source_template=None):
raise NotImplementedError()
def create_on_disk(self, source_template=None): def create_on_disk(self, source_template=None):
if source_template is None and hasattr(self.vm, 'template'): if source_template is None and hasattr(self.vm, 'template'):
source_template = self.vm.template source_template = self.vm.template
@ -250,10 +237,11 @@ class Storage(object):
old_umask = os.umask(002) old_umask = os.umask(002)
self.vm.log.info('Creating directory: {0}'.format(self.vm.dir_path)) self.vm.log.info('Creating directory: {0}'.format(self.vm.dir_path))
os.mkdir(self.vm.dir_path) os.makedirs(self.vm.dir_path)
self.create_on_disk_private_img(source_template) pool = self.get_pool()
self.create_on_disk_root_img(source_template) pool.create_on_disk_private_img(source_template)
self.reset_volatile_storage() pool.create_on_disk_root_img(source_template)
pool.reset_volatile_storage()
os.umask(old_umask) os.umask(old_umask)
@ -319,36 +307,23 @@ class Storage(object):
shutil.rmtree(self.vm.dir_path) shutil.rmtree(self.vm.dir_path)
def reset_volatile_storage(self):
# Re-create only for template based VMs
try:
if self.vm.template is not None and self.volatile_img:
if os.path.exists(self.volatile_img):
os.remove(self.volatile_img)
except AttributeError: # self.vm.template
pass
# For StandaloneVM create it only if not already exists
# (eg after backup-restore)
if hasattr(self, 'volatile_img') \
and not os.path.exists(self.volatile_img):
self.vm.log.info(
'Creating volatile image: {0}'.format(self.volatile_img))
subprocess.check_call(
[qubes.config.system_path["prepare_volatile_img_cmd"],
self.volatile_img,
str(self.root_img_size / 1024 / 1024)])
def prepare_for_vm_startup(self): def prepare_for_vm_startup(self):
self.reset_volatile_storage() pool = get_pool(self.vm.pool_name, self.vm)
pool.reset_volatile_storage()
if hasattr(self.vm, 'private_img') \ if hasattr(self.vm, 'private_img') \
and not os.path.exists(self.private_img): and not os.path.exists(self.private_img):
self.vm.log.info('Creating empty VM private image file: {0}'.format( self.vm.log.info('Creating empty VM private image file: {0}'.format(
self.private_img)) pool.private_img))
self.create_on_disk_private_img() pool.create_on_disk_private_img()
def get_pool(self):
return get_pool(self.vm.pool_name, self.vm)
def commit_template_changes(self):
pool = self.get_pool()
pool.commit_template_changes()
def get_disk_usage_one(st): def get_disk_usage_one(st):
'''Extract disk usage of one inode from its stat_result struct. '''Extract disk usage of one inode from its stat_result struct.
@ -391,56 +366,29 @@ def get_disk_usage(path):
return ret return ret
class Pool(object): class Pool(object):
def __init__(self, vm, dir_path, name): private_img_size = qubes.config.defaults['private_img_size']
assert vm is not None root_img_size = qubes.config.defaults['root_img_size']
assert dir_path is not None
assert name
def __init__(self, vm=None, name=None, **kwargs):
assert vm
assert name, "Pool name is missing"
self.vm = vm self.vm = vm
self.name = name self.name = name
self.dir_path = os.path.normpath(dir_path)
self.create_dir_if_not_exists(self.dir_path) def root_dev_config(self):
raise NotImplementedError()
self.vmdir = self.vmdir_path(vm, self.dir_path) def private_dev_config(self):
raise NotImplementedError()
appvms_path = os.path.join(self.dir_path, 'appvms') def volatile_dev_config(self):
self.create_dir_if_not_exists(appvms_path) raise NotImplementedError()
servicevms_path = os.path.join(self.dir_path, 'servicevms') def create_on_disk_private_img(self, source_template=None):
self.create_dir_if_not_exists(servicevms_path) raise NotImplementedError()
vm_templates_path = os.path.join(self.dir_path, 'vm-templates') def create_on_disk_root_img(self, source_template=None):
self.create_dir_if_not_exists(vm_templates_path) raise NotImplementedError()
# XXX there is also a class attribute on the domain classes which does
# exactly that -- which one should prevail?
def vmdir_path(self, vm, pool_dir):
""" 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
"""
if vm.is_template():
subdir = 'vm-templates'
elif vm.is_disposablevm():
subdir = 'appvms'
return os.path.join(pool_dir, subdir, vm.template.name + '-dvm')
else:
subdir = 'appvms'
return os.path.join(pool_dir, subdir, vm.name)
def create_dir_if_not_exists(self, path): def create_dir_if_not_exists(self, path):
""" Check if a directory exists in if not create it. """ Check if a directory exists in if not create it.
@ -450,6 +398,48 @@ class Pool(object):
if not os.path.exists(path): if not os.path.exists(path):
os.mkdir(path) os.mkdir(path)
def commit_template_changes(self):
raise NotImplementedError()
@staticmethod
def _copy_file(source, destination):
'''Effective file copy, preserving sparse files etc.
'''
# TODO: Windows support
# We prefer to use Linux's cp, because it nicely handles sparse files
try:
subprocess.check_call(['cp', '--reflink=auto', source, destination
])
except subprocess.CalledProcessError:
raise IOError('Error while copying {!r} to {!r}'.format(
source, destination))
def format_disk_dev(self, path, vdev, script=None, rw=True, devtype='disk',
domain=None):
return BlockDevice(path, vdev, script, rw, domain, devtype)
def reset_volatile_storage(self):
# Re-create only for template based VMs
try:
if self.vm.template is not None and self.volatile_img:
if os.path.exists(self.volatile_img):
os.remove(self.volatile_img)
except AttributeError: # self.vm.template
pass
# For StandaloneVM create it only if not already exists
# (eg after backup-restore)
if hasattr(self, 'volatile_img') \
and not os.path.exists(self.volatile_img):
self.vm.log.info(
'Creating volatile image: {0}'.format(self.volatile_img))
subprocess.check_call(
[qubes.config.system_path["prepare_volatile_img_cmd"],
self.volatile_img,
str(self.root_img_size / 1024 / 1024)])
def init_volume(self, volume_config):
raise NotImplementedError()
def pool_drivers(): def pool_drivers():
""" Return a list of EntryPoints names """ """ Return a list of EntryPoints names """

View File

@ -482,8 +482,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
self.uuid = uuid.uuid4() self.uuid = uuid.uuid4()
# Initialize VM image storage class # Initialize VM image storage class
self.storage = qubes.storage.get_pool( self.storage = qubes.storage.Storage(self)
self.pool_name, self).get_storage()
@qubes.events.handler('property-set:label') @qubes.events.handler('property-set:label')