Support for Borg --list option via borgmatic command-line to list all archives (#61).

This commit is contained in:
Dan Helfman 2018-05-26 16:09:08 -07:00
parent cc9044487b
commit af4b91a048
4 changed files with 105 additions and 12 deletions

1
NEWS
View file

@ -8,6 +8,7 @@
* #60: Add "Persistent" flag to systemd timer example. * #60: Add "Persistent" flag to systemd timer example.
* #63: Support for Borg --nobsdflags option to skip recording bsdflags (e.g. NODUMP, IMMUTABLE) in * #63: Support for Borg --nobsdflags option to skip recording bsdflags (e.g. NODUMP, IMMUTABLE) in
archive. archive.
* #61: Support for Borg --list option via borgmatic command-line to list all archives.
1.1.15 1.1.15
* Support for Borg BORG_PASSCOMMAND environment variable to read a password from an external file. * Support for Borg BORG_PASSCOMMAND environment variable to read a password from an external file.

View file

@ -36,7 +36,7 @@ def prune_archives(verbosity, dry_run, repository, storage_config, retention_con
local_path='borg', remote_path=None): local_path='borg', remote_path=None):
''' '''
Given verbosity/dry-run flags, a local or remote repository path, a storage config dict, and a Given verbosity/dry-run flags, a local or remote repository path, a storage config dict, and a
retention config dict, prune Borg archives according the the retention policy specified in that retention config dict, prune Borg archives according to the retention policy specified in that
configuration. configuration.
''' '''
remote_path_flags = ('--remote-path', remote_path) if remote_path else () remote_path_flags = ('--remote-path', remote_path) if remote_path else ()

View file

@ -5,7 +5,8 @@ import os
from subprocess import CalledProcessError from subprocess import CalledProcessError
import sys import sys
from borgmatic.borg import check, create, prune from borgmatic.borg import check as borg_check, create as borg_create, prune as borg_prune, \
list as borg_list
from borgmatic.commands import hook from borgmatic.commands import hook
from borgmatic.config import collect, convert, validate from borgmatic.config import collect, convert, validate
from borgmatic.signals import configure_signals from borgmatic.signals import configure_signals
@ -61,11 +62,17 @@ def parse_arguments(*arguments):
action='store_true', action='store_true',
help='Check archives for consistency', help='Check archives for consistency',
) )
parser.add_argument(
'-l', '--list',
dest='list',
action='store_true',
help='List archives',
)
parser.add_argument( parser.add_argument(
'-n', '--dry-run', '-n', '--dry-run',
dest='dry_run', dest='dry_run',
action='store_true', action='store_true',
help='Go through the motions, but do not actually write any changes to the repository', help='Go through the motions, but do not actually write to any repositories',
) )
parser.add_argument( parser.add_argument(
'-v', '--verbosity', '-v', '--verbosity',
@ -75,10 +82,9 @@ def parse_arguments(*arguments):
args = parser.parse_args(arguments) args = parser.parse_args(arguments)
# If any of the three action flags in the given parse arguments have been explicitly requested, # If any of the action flags are explicitly requested, leave them as-is. Otherwise, assume
# leave them as-is. Otherwise, assume defaults: Mutate the given arguments to enable all the # defaults: Mutate the given arguments to enable the default actions.
# actions. if not args.prune and not args.create and not args.check and not args.list:
if not args.prune and not args.create and not args.check:
args.prune = True args.prune = True
args.create = True args.create = True
args.check = True args.check = True
@ -101,7 +107,7 @@ def run_configuration(config_filename, args): # pragma: no cover
try: try:
local_path = location.get('local_path', 'borg') local_path = location.get('local_path', 'borg')
remote_path = location.get('remote_path') remote_path = location.get('remote_path')
create.initialize_environment(storage) borg_create.initialize_environment(storage)
hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup') hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup')
for unexpanded_repository in location['repositories']: for unexpanded_repository in location['repositories']:
@ -109,7 +115,7 @@ def run_configuration(config_filename, args): # pragma: no cover
dry_run_label = ' (dry run; not making any changes)' if args.dry_run else '' dry_run_label = ' (dry run; not making any changes)' if args.dry_run else ''
if args.prune: if args.prune:
logger.info('{}: Pruning archives{}'.format(repository, dry_run_label)) logger.info('{}: Pruning archives{}'.format(repository, dry_run_label))
prune.prune_archives( borg_prune.prune_archives(
args.verbosity, args.verbosity,
args.dry_run, args.dry_run,
repository, repository,
@ -120,7 +126,7 @@ def run_configuration(config_filename, args): # pragma: no cover
) )
if args.create: if args.create:
logger.info('{}: Creating archive{}'.format(repository, dry_run_label)) logger.info('{}: Creating archive{}'.format(repository, dry_run_label))
create.create_archive( borg_create.create_archive(
args.verbosity, args.verbosity,
args.dry_run, args.dry_run,
repository, repository,
@ -131,7 +137,7 @@ def run_configuration(config_filename, args): # pragma: no cover
) )
if args.check: if args.check:
logger.info('{}: Running consistency checks'.format(repository)) logger.info('{}: Running consistency checks'.format(repository))
check.check_archives( borg_check.check_archives(
args.verbosity, args.verbosity,
repository, repository,
storage, storage,
@ -139,6 +145,15 @@ def run_configuration(config_filename, args): # pragma: no cover
local_path=local_path, local_path=local_path,
remote_path=remote_path, remote_path=remote_path,
) )
if args.list:
logger.info('{}: Listing archives'.format(repository))
borg_list.list_archives(
args.verbosity,
repository,
storage,
local_path=local_path,
remote_path=remote_path,
)
hook.execute_hook(hooks.get('after_backup'), config_filename, 'post-backup') hook.execute_hook(hooks.get('after_backup'), config_filename, 'post-backup')
except (OSError, CalledProcessError): except (OSError, CalledProcessError):

View file

@ -0,0 +1,77 @@
from collections import OrderedDict
from flexmock import flexmock
from borgmatic.borg import list as module
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
def insert_subprocess_mock(check_call_command, **kwargs):
subprocess = flexmock(module.subprocess)
subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once()
LIST_COMMAND = ('borg', 'list', 'repo')
def test_list_archives_calls_borg_with_parameters():
insert_subprocess_mock(LIST_COMMAND)
module.list_archives(
verbosity=None,
repository='repo',
storage_config={},
)
def test_list_archives_with_verbosity_some_calls_borg_with_info_parameter():
insert_subprocess_mock(LIST_COMMAND + ('--info',))
module.list_archives(
repository='repo',
storage_config={},
verbosity=VERBOSITY_SOME,
)
def test_list_archives_with_verbosity_lots_calls_borg_with_debug_parameter():
insert_subprocess_mock(LIST_COMMAND + ('--debug',))
module.list_archives(
repository='repo',
storage_config={},
verbosity=VERBOSITY_LOTS,
)
def test_list_archives_with_local_path_calls_borg_via_local_path():
insert_subprocess_mock(('borg1',) + LIST_COMMAND[1:])
module.list_archives(
verbosity=None,
repository='repo',
storage_config={},
local_path='borg1',
)
def test_list_archives_with_remote_path_calls_borg_with_remote_path_parameters():
insert_subprocess_mock(LIST_COMMAND + ('--remote-path', 'borg1'))
module.list_archives(
verbosity=None,
repository='repo',
storage_config={},
remote_path='borg1',
)
def test_list_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
storage_config = {'lock_wait': 5}
insert_subprocess_mock(LIST_COMMAND + ('--lock-wait', '5'))
module.list_archives(
verbosity=None,
repository='repo',
storage_config=storage_config,
)