-
-
Notifications
You must be signed in to change notification settings - Fork 55
Description
Design
This is a deisign proposal for the storage subsystem for QubesOS R4.0. For previous discussions see also the PR: Allow defining and using custom Storage types
Requirements
- Multiple storage implementations like file based (current storage method), LVM Thin Pools, Btrfs, ZFS ...
- 3rd parties can develop custom storage implementations
- uses entry-points for discovery of available storage backends make extensions discoverable by setuptools' entrypoints #1238
- Be independent enough to work on it's own in a separate storage domain
- The AppVM needs to be able to access a read only root image of the TempalteVM, so it can verify it.
- Import/Export storage volumes between pools
- Domains can reuse different pools for different
StorageVolumes - Differentiate between permanent and temporary attached devices
- Handle removable devices (or should we instead save permanent?)
- Pool should know all devices it handles.
- Add Volume.verify()
- Update here documented Interfaces
- Revert, list revert points
Volume
Encapsulates all data about a volume for serialization to qubes.xml and libvirt config.
Interface
class Volume:
devtype = 'disk' # Used for libvirt <disk device="devtype">
domain = None # None is currently the same as dom0
name = 'root' # (root|private|volatile|linux-kernel|foo)
pool = 'qubes_dom0' # Pool name
rw = True
script = None # Libvirt' s <disk><script /></disk>
size = 0
usage = 0
vid = None # some kind of id must be unique in a pool. XenStorage uses path for that
volume_type = 'read-write' # (read-write|read-only|origin|snapshot|volatile)
@property
def config(self):
''' return config data for serialization to qubes.xml as (<volume_config>) '''
return {'name': self.name,
'pool': self.pool,
'volume_type': self.volume_type}
def block_device(self):
''' Return :py:class:`qubes.devices.BlockDevice` for serialization in
the libvirt XML template as <disk>.
'''
return BlockDevice(self.path, self.name, self.script, self.rw,
self.domain, self.devtype)
Pool
A Pool is used to manage different kind of volumes (File based/LVM/Btrfs/...).
Interface
3rd Parties providing own storage implementations will need to implement the following interface.
class Pool:
def init(self, vm, name, **kwargs) # TODO Refactor vm out
def init_volume(volume_config) # Creates a Volume object from given volume_config
def create(self, volume) # Create volume on disk
def commit_template_changes(volume)
def clone(source_volume, target_volume)
def rename(volume, old_name, new_name)
def start(volume)
def stop(volume)
@property
def volumes(self): # all volumes managed by this pool, TODO imlement thisStorage
The Storage class provides managment methods for domain's volumes. The method are called by the volume at the appropriate time. Currently it's in qubes/storage/__init__.py, but I'm considering to move it somewhere else, or make it a part of QubesVM, because most of the methods just iterate over self.vm.volumes and execute a method. See also my current Storage version
Interface
class Storage:
def __init__(vm):
for conf in vm.volumes_config: # simplified!
vm.volumes[conf['name']] = pool.init_volume(conf)
def kernels_dir() # backward compatibility will be removed
def create_on_disk(self) # Create volumes on disk, TODO rename to create()
os.makedirs(self.vm.dir_path)
for volume in self.vm.volumes.values():
self.get_pool(volume).create(volume)
os.umask(old_umask)
def commit_template_changes()
for v in self.vm.volumes.values():
if v.volume_type == 'origin':
self.get_pool(v).commit_template_changes(v)
def clone(self, src_vm)
def rename(old_name, new_name)
def start()
def stop()
def verify_files(): # Just does some sanity checks currently.
def get_pool(volume): # Helper method
return qubes.storage.get_pool(volume, self.vm)
# The above will be replaced once vm is refactored out of the pool with:
return qubes.storage.get_pool(volume, vm.pool_config[volume.pool])Further Details
QubesVM.volume_configwould contain a dict {'volume_name':{config}} from the xml configuration for the current domain.QubesVM.volumesis a dict containing{ 'root_img' : Volume, 'private_img' : Volume, ...}Qubes.pool_configcontains the pool config parsed from qubes.xml. Will be replacedQubes.pool_configwithQubes.poolscontaining*Pool
Open Questions
- What should be the parameter to
Volume.resize()? There might be volume implementations which have strategies for shrinking. Should resize also accept smaller sizes as the current one? Or should we even haveextend()andshrink()
EDITS:
- 2016-03-17 Change signature of
Storage.import() - 2016-04-10 Document the current API