diff --git a/contrib/tools/btrbk_restore_raw.py b/contrib/tools/btrbk_restore_raw.py index cd90b99..07f68bc 100755 --- a/contrib/tools/btrbk_restore_raw.py +++ b/contrib/tools/btrbk_restore_raw.py @@ -11,6 +11,9 @@ logger = logging.getLogger(__name__) class TransformProcess: def run(self, bfile, options, **kw): + return subprocess.Popen(self.get_cmd(bfile, options), **kw) + + def get_cmd(self, bfile, options): raise NotImplementedError() @classmethod @@ -20,13 +23,14 @@ class TransformProcess: class TransformOpensslDecrypt(TransformProcess): @staticmethod - def run(bfile, options, **kw): - return subprocess.Popen([ + def get_cmd(bfile, options): + return [ 'openssl', 'enc', '-d', '-' + bfile.info['cipher'], '-K', - open(options.openssl_keyfile, 'r').read(), '-iv', bfile.info['iv']], **kw) + open(options.openssl_keyfile, 'r').read(), '-iv', bfile.info['iv'] + ] - @classmethod - def add_parser_options(cls, parser): + @staticmethod + def add_parser_options(parser): parser.add_argument('--openssl-keyfile', help="path to private encryption key file") @@ -34,14 +38,18 @@ class TransformDecompress(TransformProcess): def __init__(self, program): self.p = program - def run(self, bfile, options, **kw): - return subprocess.Popen([self.p, '-d'], **kw) + def get_cmd(self, bfile, options): + return [self.p, '-d'] class TransformBtrfsReceive(TransformProcess): + @classmethod + def run(cls, bfile, options, **kw): + return subprocess.Popen(cls.get_cmd(bfile, options), **kw) + @staticmethod - def run(bfile, options, **kw): - return subprocess.Popen(['btrfs', 'receive', options.restore_dir], **kw) + def get_cmd(bfile, options): + return ['btrfs', 'receive', options.restore_dir] TRANSFORMERS = ( @@ -96,6 +104,13 @@ class BtrfsPipeline: if msg: logger.error(f"error running {p.args}: {msg}") + def get_cmd(self, options): + command_pipe = [['cat', self.bfile.data_file]] + for transformer in self.processors: + command_pipe.append(transformer.get_cmd(self.bfile, options)) + command_pipe.append(TransformBtrfsReceive.get_cmd(self.bfile, options)) + return ' | '.join(' '.join(x) for x in command_pipe) + class BackupFile: def __init__(self, path): @@ -132,11 +147,14 @@ class BackupFile: def restore_file(self, options): assert self.info.get('TYPE') == 'raw' assert not self.info.get('INCOMPLETE') - logger.info(f"restoring backup {os.path.basename(self.data_file)}") pipeline = BtrfsPipeline(self) for transformer in self.get_transformers(): pipeline.append(transformer) - pipeline.run(options) + if options.dry_run: + print(pipeline.get_cmd(options)) + else: + logger.info(f"restoring backup {os.path.basename(self.data_file)}") + pipeline.run(options) self.is_restored = True @@ -175,13 +193,21 @@ def main(): parser = argparse.ArgumentParser(description="restore btrbk raw backup") parser.add_argument('backup', help="backup file to restore; for incremental" " backups the parent files must be in the same directory") - parser.add_argument('restore_dir', help="target directory for restored subvolumes (path argument for \"btrfs receive\")") - parser.add_argument('--ignore-missing', action='store_true', help="do not fail on missing parent snapshots") + parser.add_argument('restore_dir', help="target directory for restored subvolumes" + " (path argument for \"btrfs receive\")") + parser.add_argument('--ignore-missing', action='store_true', + help="do not fail on missing parent snapshots") + parser.add_argument('--dry-run', '-n', action='store_true', + help="print commmands that would be exectuted") for transformer in TRANSFORMERS: transformer.add_parser_options(parser) args = parser.parse_args() + + if args.dry_run: + logger.setLevel('ERROR') + restore_from_path(args.backup, args)