storage/callback: make pylint happy
This commit is contained in:
parent
efa0d7c257
commit
5530265b27
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
import importlib
|
|
||||||
import json
|
import json
|
||||||
from shlex import quote
|
from shlex import quote
|
||||||
|
|
||||||
@ -42,7 +41,7 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
|
|
||||||
**Integration tests**:
|
**Integration tests**:
|
||||||
(all of these tests assume the `qubes_callback.json.example` configuration)
|
(all of these tests assume the `qubes_callback.json.example` configuration)
|
||||||
|
|
||||||
Tests that should **fail**:
|
Tests that should **fail**:
|
||||||
```
|
```
|
||||||
qvm-pool -a test callback
|
qvm-pool -a test callback
|
||||||
@ -51,14 +50,14 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
qvm-pool -o conf_id=testing-fail-missing-all -a test callback
|
qvm-pool -o conf_id=testing-fail-missing-all -a test callback
|
||||||
qvm-pool -o conf_id=testing-fail-missing-bdriver-args -a test callback
|
qvm-pool -o conf_id=testing-fail-missing-bdriver-args -a test callback
|
||||||
```
|
```
|
||||||
|
|
||||||
Tests that should **work**:
|
Tests that should **work**:
|
||||||
```
|
```
|
||||||
qvm-pool -o conf_id=testing-succ-file-01 -a test callback
|
qvm-pool -o conf_id=testing-succ-file-01 -a test callback
|
||||||
qvm-pool
|
qvm-pool
|
||||||
ls /mnt/test01
|
ls /mnt/test01
|
||||||
qvm-pool -r test && sudo rm -rf /mnt/test01
|
qvm-pool -r test && sudo rm -rf /mnt/test01
|
||||||
|
|
||||||
echo '#!/bin/bash'$'\n''i=0 ; for arg in "$@" ; do echo "$i: $arg" >> /tmp/callback.log ; (( i++)) ; done ; exit 0' > /usr/bin/testCbLogArgs && chmod +x /usr/bin/testCbLogArgs
|
echo '#!/bin/bash'$'\n''i=0 ; for arg in "$@" ; do echo "$i: $arg" >> /tmp/callback.log ; (( i++)) ; done ; exit 0' > /usr/bin/testCbLogArgs && chmod +x /usr/bin/testCbLogArgs
|
||||||
rm -f /tmp/callback.log
|
rm -f /tmp/callback.log
|
||||||
qvm-pool -o conf_id=testing-succ-file-02 -a test callback
|
qvm-pool -o conf_id=testing-succ-file-02 -a test callback
|
||||||
@ -81,14 +80,14 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
qvm-shutdown --wait test-vm && qvm-remove test-vm
|
qvm-shutdown --wait test-vm && qvm-remove test-vm
|
||||||
qvm-pool -r test && sudo rm -rf /mnt/test02
|
qvm-pool -r test && sudo rm -rf /mnt/test02
|
||||||
less /tmp/callback.log (2x on_volume_stop, 2x on_volume_remove, on_destroy should be added)
|
less /tmp/callback.log (2x on_volume_stop, 2x on_volume_remove, on_destroy should be added)
|
||||||
|
|
||||||
qvm-pool -o conf_id=testing-succ-file-03 -a test callback
|
qvm-pool -o conf_id=testing-succ-file-03 -a test callback
|
||||||
qvm-pool
|
qvm-pool
|
||||||
ls /mnt/test03
|
ls /mnt/test03
|
||||||
less /tmp/callback.log (on_ctor & on_setup should be there, no more arguments)
|
less /tmp/callback.log (on_ctor & on_setup should be there, no more arguments)
|
||||||
qvm-pool -r test && sudo rm -rf /mnt/test03
|
qvm-pool -r test && sudo rm -rf /mnt/test03
|
||||||
less /tmp/callback.log (nothing should have been added)
|
less /tmp/callback.log (nothing should have been added)
|
||||||
|
|
||||||
#luks pool test:
|
#luks pool test:
|
||||||
#(make sure /mnt/test.key & /mnt/test.luks don't exist)
|
#(make sure /mnt/test.key & /mnt/test.luks don't exist)
|
||||||
qvm-pool -o conf_id=testing-succ-file-luks -a tluks callback
|
qvm-pool -o conf_id=testing-succ-file-luks -a tluks callback
|
||||||
@ -111,7 +110,7 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
qvm-pool -r tluks
|
qvm-pool -r tluks
|
||||||
sudo cryptsetup status test-luks
|
sudo cryptsetup status test-luks
|
||||||
ls -l /mnt/
|
ls -l /mnt/
|
||||||
|
|
||||||
#ephemeral luks pool test (key in RAM / lost on reboot):
|
#ephemeral luks pool test (key in RAM / lost on reboot):
|
||||||
qvm-pool -o conf_id=testing-succ-file-luks-eph -a teph callback (executes setup() twice due to signal_back)
|
qvm-pool -o conf_id=testing-succ-file-luks-eph -a teph callback (executes setup() twice due to signal_back)
|
||||||
ls /mnt/
|
ls /mnt/
|
||||||
@ -163,7 +162,7 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
''' # pylint: disable=protected-access
|
''' # pylint: disable=protected-access
|
||||||
|
|
||||||
driver = 'callback'
|
driver = 'callback'
|
||||||
config_path='/etc/qubes_callback.json'
|
config_path = '/etc/qubes_callback.json'
|
||||||
|
|
||||||
def __init__(self, *, name, conf_id):
|
def __init__(self, *, name, conf_id):
|
||||||
'''Constructor.
|
'''Constructor.
|
||||||
@ -217,37 +216,41 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
return bool(cmd and cmd != '-')
|
return bool(cmd and cmd != '-')
|
||||||
|
|
||||||
def _init(self, callback=True):
|
def _init(self, callback=True):
|
||||||
#late initialization on first use for e.g. decryption on first usage request
|
''' Late storage initialization on first use for e.g. decryption on first usage request.
|
||||||
|
:param callback: Whether to trigger the `on_sinit` callback or not.
|
||||||
|
'''
|
||||||
#maybe TODO: if this function is meant to be run in parallel (are Pool operations asynchronous?), a function lock is required!
|
#maybe TODO: if this function is meant to be run in parallel (are Pool operations asynchronous?), a function lock is required!
|
||||||
if callback:
|
if callback:
|
||||||
self._callback('on_sinit')
|
self._callback('on_sinit')
|
||||||
self._cb_requires_init = False
|
self._cb_requires_init = False
|
||||||
|
|
||||||
def _assertInitialized(self, **kwargs):
|
def _assert_initialized(self, **kwargs):
|
||||||
if self._cb_requires_init:
|
if self._cb_requires_init:
|
||||||
self._init(**kwargs)
|
self._init(**kwargs)
|
||||||
|
|
||||||
def _callback(self, cb, cb_args=[], log=logging.getLogger('qubes.storage.callback')):
|
def _callback(self, cb, cb_args=None, log=logging.getLogger('qubes.storage.callback')):
|
||||||
'''Run a callback.
|
'''Run a callback.
|
||||||
:param cb: Callback identifier string.
|
:param cb: Callback identifier string.
|
||||||
:param cb_args: Optional arguments to pass to the command as last arguments.
|
:param cb_args: Optional list of arguments to pass to the command as last arguments.
|
||||||
Only passed on for the generic command specified as `cmd`, not for `on_xyz` callbacks.
|
Only passed on for the generic command specified as `cmd`, not for `on_xyz` callbacks.
|
||||||
'''
|
'''
|
||||||
if self._cb_ctor_done:
|
if self._cb_ctor_done:
|
||||||
cmd = self._cb_conf.get(cb)
|
cmd = self._cb_conf.get(cb)
|
||||||
args = [] #on_xyz callbacks should never receive arguments
|
args = [] #on_xyz callbacks should never receive arguments
|
||||||
if not cmd:
|
if not cmd:
|
||||||
|
if cb_args is None:
|
||||||
|
cb_args = []
|
||||||
cmd = self._cb_conf.get('cmd')
|
cmd = self._cb_conf.get('cmd')
|
||||||
args = [ self.name, self._cb_conf['bdriver'], cb, self._cb_cmd_arg, *cb_args ]
|
args = [self.name, self._cb_conf['bdriver'], cb, self._cb_cmd_arg, *cb_args]
|
||||||
if cmd and cmd != '-':
|
if cmd and cmd != '-':
|
||||||
args = filter(None, args)
|
args = filter(None, args)
|
||||||
args = ' '.join(quote(str(a)) for a in args)
|
args = ' '.join(quote(str(a)) for a in args)
|
||||||
cmd = ' '.join(filter(None, [cmd, args]))
|
cmd = ' '.join(filter(None, [cmd, args]))
|
||||||
log.info('callback driver executing (%s, %s %s): %s' % (self._cb_conf_id, cb, cb_args, cmd))
|
log.info('callback driver executing (%s, %s %s): %s', self._cb_conf_id, cb, cb_args, cmd)
|
||||||
res = subprocess.run(['/bin/bash', '-c', cmd], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
res = subprocess.run(['/bin/bash', '-c', cmd], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
||||||
#stdout & stderr are reported if the exit code check fails
|
#stdout & stderr are reported if the exit code check fails
|
||||||
log.debug('callback driver stdout (%s, %s %s): %s' % (self._cb_conf_id, cb, cb_args, res.stdout))
|
log.debug('callback driver stdout (%s, %s %s): %s', self._cb_conf_id, cb, cb_args, res.stdout)
|
||||||
log.debug('callback driver stderr (%s, %s %s): %s' % (self._cb_conf_id, cb, cb_args, res.stderr))
|
log.debug('callback driver stderr (%s, %s %s): %s', self._cb_conf_id, cb, cb_args, res.stderr)
|
||||||
if self._cb_conf.get('signal_back', False) is True:
|
if self._cb_conf.get('signal_back', False) is True:
|
||||||
self._process_signals(res.stdout, log)
|
self._process_signals(res.stdout, log)
|
||||||
|
|
||||||
@ -258,13 +261,8 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
'''
|
'''
|
||||||
for line in out.splitlines():
|
for line in out.splitlines():
|
||||||
if line == 'SIGNAL_setup':
|
if line == 'SIGNAL_setup':
|
||||||
log.info('callback driver processing SIGNAL_setup for %s' % self._cb_conf_id)
|
log.info('callback driver processing SIGNAL_setup for %s', self._cb_conf_id)
|
||||||
self.setup(callback=False)
|
self._setup_cb(callback=False)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
s = super()
|
|
||||||
if hasattr(s, '__del__'):
|
|
||||||
s.__del__()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def config(self):
|
def config(self):
|
||||||
@ -275,7 +273,7 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
ret = self._cb_impl.destroy()
|
ret = self._cb_impl.destroy()
|
||||||
self._callback('on_destroy')
|
self._callback('on_destroy')
|
||||||
return ret
|
return ret
|
||||||
@ -283,12 +281,15 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
def init_volume(self, vm, volume_config):
|
def init_volume(self, vm, volume_config):
|
||||||
return CallbackVolume(self, self._cb_impl.init_volume(vm, volume_config))
|
return CallbackVolume(self, self._cb_impl.init_volume(vm, volume_config))
|
||||||
|
|
||||||
def setup(self, callback=True):
|
def _setup_cb(self, callback=True):
|
||||||
if callback:
|
if callback:
|
||||||
self._callback('on_setup')
|
self._callback('on_setup')
|
||||||
self._assertInitialized(callback=False) #setup is assumed to include initialization
|
self._assert_initialized(callback=False) #setup is assumed to include storage initialization
|
||||||
return self._cb_impl.setup()
|
return self._cb_impl.setup()
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
return self._setup_cb()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volumes(self):
|
def volumes(self):
|
||||||
for vol in self._cb_impl.volumes:
|
for vol in self._cb_impl.volumes:
|
||||||
@ -304,22 +305,19 @@ class CallbackPool(qubes.storage.Pool):
|
|||||||
def included_in(self, app):
|
def included_in(self, app):
|
||||||
if self._cb_requires_init:
|
if self._cb_requires_init:
|
||||||
return None
|
return None
|
||||||
else:
|
return self._cb_impl.included_in(app)
|
||||||
return self._cb_impl.included_in(app)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self):
|
def size(self):
|
||||||
if self._cb_requires_init:
|
if self._cb_requires_init:
|
||||||
return None
|
return None
|
||||||
else:
|
return self._cb_impl.size
|
||||||
return self._cb_impl.size
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def usage(self):
|
def usage(self):
|
||||||
if self._cb_requires_init:
|
if self._cb_requires_init:
|
||||||
return None
|
return None
|
||||||
else:
|
return self._cb_impl.usage
|
||||||
return self._cb_impl.usage
|
|
||||||
|
|
||||||
#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
|
||||||
@ -361,72 +359,72 @@ class CallbackVolume:
|
|||||||
self._cb_pool = pool
|
self._cb_pool = pool
|
||||||
self._cb_impl = impl
|
self._cb_impl = impl
|
||||||
|
|
||||||
def _assertInitialized(self, **kwargs):
|
def _assert_initialized(self, **kwargs):
|
||||||
return self._cb_pool._assertInitialized(**kwargs)
|
return self._cb_pool._assert_initialized(**kwargs) # pylint: disable=protected-access
|
||||||
|
|
||||||
def _callback(self, cb, cb_args=[], **kwargs):
|
def _callback(self, cb, cb_args=None, **kwargs):
|
||||||
vol_args = [ *cb_args, self.name, self.vid ]
|
if cb_args is None:
|
||||||
return self._cb_pool._callback(cb, cb_args=vol_args, **kwargs)
|
cb_args = []
|
||||||
|
vol_args = [self.name, self.vid, *cb_args]
|
||||||
|
return self._cb_pool._callback(cb, cb_args=vol_args, **kwargs) # pylint: disable=protected-access
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
self._callback('on_volume_create')
|
self._callback('on_volume_create')
|
||||||
return self._cb_impl.create()
|
return self._cb_impl.create()
|
||||||
|
|
||||||
def remove(self):
|
def remove(self):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
ret = self._cb_impl.remove()
|
ret = self._cb_impl.remove()
|
||||||
self._callback('on_volume_remove')
|
self._callback('on_volume_remove')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def resize(self, size):
|
def resize(self, size):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
self._callback('on_volume_resize', cb_args=[size])
|
self._callback('on_volume_resize', cb_args=[size])
|
||||||
return self._cb_impl.resize(size)
|
return self._cb_impl.resize(size)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
self._callback('on_volume_start')
|
self._callback('on_volume_start')
|
||||||
return self._cb_impl.start()
|
return self._cb_impl.start()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
ret = self._cb_impl.stop()
|
ret = self._cb_impl.stop()
|
||||||
self._callback('on_volume_stop')
|
self._callback('on_volume_stop')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def import_data(self):
|
def import_data(self):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
self._callback('on_volume_import_data')
|
self._callback('on_volume_import_data')
|
||||||
return self._cb_impl.import_data()
|
return self._cb_impl.import_data()
|
||||||
|
|
||||||
def import_data_end(self, success):
|
def import_data_end(self, success):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
ret = self._cb_impl.import_data_end(success)
|
ret = self._cb_impl.import_data_end(success)
|
||||||
self._callback('on_volume_import_data_end', cb_args=[success])
|
self._callback('on_volume_import_data_end', cb_args=[success])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def import_volume(self, src_volume):
|
def import_volume(self, src_volume):
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
self._callback('on_volume_import', cb_args=[src_volume.vid])
|
self._callback('on_volume_import', cb_args=[src_volume.vid])
|
||||||
return self._cb_impl.import_volume(src_volume)
|
return self._cb_impl.import_volume(src_volume)
|
||||||
|
|
||||||
def is_dirty(self):
|
def is_dirty(self):
|
||||||
if self._cb_pool._cb_requires_init:
|
if self._cb_pool._cb_requires_init: # pylint: disable=protected-access
|
||||||
return False
|
return False
|
||||||
else:
|
return self._cb_impl.is_dirty()
|
||||||
return self._cb_impl.is_dirty()
|
|
||||||
|
|
||||||
def is_outdated(self):
|
def is_outdated(self):
|
||||||
if self._cb_pool._cb_requires_init:
|
if self._cb_pool._cb_requires_init: # pylint: disable=protected-access
|
||||||
return False
|
return False
|
||||||
else:
|
return self._cb_impl.is_outdated()
|
||||||
return self._cb_impl.is_outdated()
|
|
||||||
|
|
||||||
#remaining method & attribute delegation
|
#remaining method & attribute delegation
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name in [ 'block_device', 'verify', 'revert', 'export' ]:
|
if name in ['block_device', 'verify', 'revert', 'export']:
|
||||||
self._assertInitialized()
|
self._assert_initialized()
|
||||||
return getattr(self._cb_impl, name)
|
return getattr(self._cb_impl, name)
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
|
Loading…
Reference in New Issue
Block a user