storage/callback: various fixes

- Removed all own class attributes to avoid name clashes with delegated
class attributes.
- Implemented the previously missing Pool.usage_details property.
- Shadowed all class attributes as instance properties. This is required
as the parent classes enforce the class attributes upon the
CallbackPool & CallbackVolume classes, but they need to be delegated to
the class of the _cb_impl object. We also cannot implement them as class
attributes in CallbackVolume & CallbackPool as they need to work for
arbitrary backend drivers and two backend drivers must not interfere with
each other. Possible alternative: One could dynamically create classes.
This commit is contained in:
3hhh 2020-07-11 13:01:22 +02:00
parent d9f1bced22
commit e5838dbd97
No known key found for this signature in database
GPG Key ID: EB03A691DB2F0833

View File

@ -189,9 +189,6 @@ class CallbackPool(qubes.storage.Pool):
``` ```
''' '''
driver = 'callback'
config_path = '/etc/qubes_callback.json'
def __init__(self, *, name, conf_id): def __init__(self, *, name, conf_id):
'''Constructor. '''Constructor.
:param conf_id: Identifier as found inside the user-controlled configuration at `/etc/qubes_callback.json`. :param conf_id: Identifier as found inside the user-controlled configuration at `/etc/qubes_callback.json`.
@ -209,21 +206,22 @@ class CallbackPool(qubes.storage.Pool):
raise qubes.storage.StoragePoolException('conf_id is no String. VM attack?!') raise qubes.storage.StoragePoolException('conf_id is no String. VM attack?!')
self._cb_conf_id = conf_id #: Configuration ID as passed to `__init__()`. self._cb_conf_id = conf_id #: Configuration ID as passed to `__init__()`.
with open(CallbackPool.config_path) as json_file: config_path = '/etc/qubes_callback.json'
with open(config_path) as json_file:
conf_all = json.load(json_file) conf_all = json.load(json_file)
if not isinstance(conf_all, dict): if not isinstance(conf_all, dict):
raise qubes.storage.StoragePoolException('The file %s is supposed to define a dict.' % CallbackPool.config_path) raise qubes.storage.StoragePoolException('The file %s is supposed to define a dict.' % config_path)
try: try:
self._cb_conf = conf_all[self._cb_conf_id] #: Dictionary holding all configuration for the given _cb_conf_id. self._cb_conf = conf_all[self._cb_conf_id] #: Dictionary holding all configuration for the given _cb_conf_id.
except KeyError: except KeyError:
#we cannot throw KeyErrors as we'll otherwise generate incorrect error messages @qubes.app._get_pool() #we cannot throw KeyErrors as we'll otherwise generate incorrect error messages @qubes.app._get_pool()
raise qubes.storage.StoragePoolException('The specified conf_id %s could not be found inside %s.' % (self._cb_conf_id, CallbackPool.config_path)) raise qubes.storage.StoragePoolException('The specified conf_id %s could not be found inside %s.' % (self._cb_conf_id, config_path))
try: try:
bdriver = self._cb_conf['bdriver'] bdriver = self._cb_conf['bdriver']
except KeyError: except KeyError:
raise qubes.storage.StoragePoolException('Missing bdriver for the conf_id %s inside %s.' % (self._cb_conf_id, CallbackPool.config_path)) raise qubes.storage.StoragePoolException('Missing bdriver for the conf_id %s inside %s.' % (self._cb_conf_id, config_path))
self._cb_cmd_arg = json.dumps(self._cb_conf, sort_keys=True, indent=2) #: Full configuration as string in the format required by _callback(). self._cb_cmd_arg = json.dumps(self._cb_conf, sort_keys=True, indent=2) #: Full configuration as string in the format required by _callback().
@ -344,7 +342,7 @@ class CallbackPool(qubes.storage.Pool):
def config(self): def config(self):
return { return {
'name': self.name, 'name': self.name,
'driver': CallbackPool.driver, 'driver': 'callback',
'conf_id': self._cb_conf_id, 'conf_id': self._cb_conf_id,
} }
@ -395,6 +393,30 @@ class CallbackPool(qubes.storage.Pool):
return None return None
return self._cb_impl.usage return self._cb_impl.usage
@property
def usage_details(self):
if self._cb_requires_init:
return {}
return self._cb_impl.usage_details
#shadow all qubes.storage.Pool class attributes as instance properties
#NOTE: this will cause a subtle difference to using an actual _cb_impl instance: CallbackPool.private_img_size will return a property object, Pool.private_img_size the actual value
@property
def private_img_size(self):
return self._cb_impl.private_img_size
@private_img_size.setter
def private_img_size(self, private_img_size):
self._cb_impl.private_img_size = private_img_size
@property
def root_img_size(self):
return self._cb_impl.root_img_size
@root_img_size.setter
def root_img_size(self, root_img_size):
self._cb_impl.root_img_size = root_img_size
#remaining method & attribute delegation ("delegation pattern") #remaining method & attribute delegation ("delegation pattern")
#Convention: The methods of this object have priority over the delegated object's methods. All attributes are #Convention: The methods of this object have priority over the delegated object's methods. All attributes are
# passed to the delegated object unless their name starts with '_cb_'. # passed to the delegated object unless their name starts with '_cb_'.
@ -552,6 +574,48 @@ class CallbackVolume(qubes.storage.Volume):
yield from self._assert_initialized() yield from self._assert_initialized()
return (yield from coro_maybe(self._cb_impl.revert(revision=revision))) return (yield from coro_maybe(self._cb_impl.revert(revision=revision)))
#shadow all qubes.storage.Volume class attributes as instance properties
#NOTE: this will cause a subtle difference to using an actual _cb_impl instance: CallbackVolume.devtype will return a property object, Volume.devtype the actual value
@property
def devtype(self):
return self._cb_impl.devtype
@devtype.setter
def devtype(self, devtype):
self._cb_impl.devtype = devtype
@property
def domain(self):
return self._cb_impl.domain
@domain.setter
def domain(self, domain):
self._cb_impl.domain = domain
@property
def path(self):
return self._cb_impl.path
@path.setter
def path(self, path):
self._cb_impl.path = path
@property
def script(self):
return self._cb_impl.script
@script.setter
def script(self, script):
self._cb_impl.script = script
@property
def usage(self):
return self._cb_impl.usage
@usage.setter
def usage(self, usage):
self._cb_impl.usage = usage
#remaining method & attribute delegation #remaining method & attribute delegation
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self._cb_impl, name) return getattr(self._cb_impl, name)