Support for several more borgmatic/borg info command-line flags (#193).
This commit is contained in:
parent
c644270599
commit
86dbc00cbe
6 changed files with 134 additions and 35 deletions
6
NEWS
6
NEWS
|
@ -1,6 +1,8 @@
|
||||||
1.3.11.dev0
|
1.3.11.dev0
|
||||||
* #193: Pass through several "borg list" flags like --short, --format, --sort-by, --first, --last,
|
* #193: Pass through several "borg list" and "borg info" flags like --short, --format, --sort-by,
|
||||||
etc. via borgmatic list command-line flags.
|
--first, --last, etc. via borgmatic command-line flags.
|
||||||
|
* Add borgmatic info --repository and --archive command-line flags to display info for individual
|
||||||
|
repositories or archives.
|
||||||
|
|
||||||
1.3.10
|
1.3.10
|
||||||
* #198: Fix for Borg create error output not showing up at borgmatic verbosity level zero.
|
* #198: Fix for Borg create error output not showing up at borgmatic verbosity level zero.
|
||||||
|
|
|
@ -1,26 +1,44 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from borgmatic.borg.flags import make_flags, make_flags_from_arguments
|
||||||
from borgmatic.execute import execute_command
|
from borgmatic.execute import execute_command
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def display_archives_info(
|
def display_archives_info(
|
||||||
repository, storage_config, local_path='borg', remote_path=None, json=False
|
repository, storage_config, info_arguments, local_path='borg', remote_path=None
|
||||||
):
|
):
|
||||||
'''
|
'''
|
||||||
Given a local or remote repository path, and a storage config dict, display summary information
|
Given a local or remote repository path, a storage config dict, and the arguments to the info
|
||||||
for Borg archives in the repository or return JSON summary information.
|
action, display summary information for Borg archives in the repository or return JSON summary
|
||||||
|
information.
|
||||||
'''
|
'''
|
||||||
lock_wait = storage_config.get('lock_wait', None)
|
lock_wait = storage_config.get('lock_wait', None)
|
||||||
|
|
||||||
full_command = (
|
full_command = (
|
||||||
(local_path, 'info', repository)
|
(
|
||||||
+ (('--remote-path', remote_path) if remote_path else ())
|
local_path,
|
||||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
'info',
|
||||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO and not json else ())
|
'::'.join((repository, info_arguments.archive))
|
||||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) and not json else ())
|
if info_arguments.archive
|
||||||
+ (('--json',) if json else ())
|
else repository,
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
('--info',)
|
||||||
|
if logger.getEffectiveLevel() == logging.INFO and not info_arguments.json
|
||||||
|
else ()
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
('--debug', '--show-rc')
|
||||||
|
if logger.isEnabledFor(logging.DEBUG) and not info_arguments.json
|
||||||
|
else ()
|
||||||
|
)
|
||||||
|
+ make_flags('remote-path', remote_path)
|
||||||
|
+ make_flags('lock-wait', lock_wait)
|
||||||
|
+ make_flags_from_arguments(info_arguments, excludes=('repository', 'archive'))
|
||||||
)
|
)
|
||||||
|
|
||||||
return execute_command(full_command, output_log_level=None if json else logging.WARNING)
|
return execute_command(
|
||||||
|
full_command, output_log_level=None if info_arguments.json else logging.WARNING
|
||||||
|
)
|
||||||
|
|
|
@ -224,7 +224,7 @@ def parse_arguments(*unparsed_arguments):
|
||||||
extract_group = extract_parser.add_argument_group('extract arguments')
|
extract_group = extract_parser.add_argument_group('extract arguments')
|
||||||
extract_group.add_argument(
|
extract_group.add_argument(
|
||||||
'--repository',
|
'--repository',
|
||||||
help='Path of repository to use, defaults to the configured repository if there is only one',
|
help='Path of repository to extract, defaults to the configured repository if there is only one',
|
||||||
)
|
)
|
||||||
extract_group.add_argument('--archive', help='Name of archive to operate on', required=True)
|
extract_group.add_argument('--archive', help='Name of archive to operate on', required=True)
|
||||||
extract_group.add_argument(
|
extract_group.add_argument(
|
||||||
|
@ -254,9 +254,9 @@ def parse_arguments(*unparsed_arguments):
|
||||||
list_group = list_parser.add_argument_group('list arguments')
|
list_group = list_parser.add_argument_group('list arguments')
|
||||||
list_group.add_argument(
|
list_group.add_argument(
|
||||||
'--repository',
|
'--repository',
|
||||||
help='Path of repository to use, defaults to the configured repository if there is only one',
|
help='Path of repository to list, defaults to the configured repository if there is only one',
|
||||||
)
|
)
|
||||||
list_group.add_argument('--archive', help='Name of archive to operate on')
|
list_group.add_argument('--archive', help='Name of archive to list')
|
||||||
list_group.add_argument(
|
list_group.add_argument(
|
||||||
'--short', default=False, action='store_true', help='Output only archive or path names'
|
'--short', default=False, action='store_true', help='Output only archive or path names'
|
||||||
)
|
)
|
||||||
|
@ -301,9 +301,34 @@ def parse_arguments(*unparsed_arguments):
|
||||||
add_help=False,
|
add_help=False,
|
||||||
)
|
)
|
||||||
info_group = info_parser.add_argument_group('info arguments')
|
info_group = info_parser.add_argument_group('info arguments')
|
||||||
|
info_group.add_argument(
|
||||||
|
'--repository',
|
||||||
|
help='Path of repository to show info for, defaults to the configured repository if there is only one',
|
||||||
|
)
|
||||||
|
info_group.add_argument('--archive', help='Name of archive to show info for')
|
||||||
info_group.add_argument(
|
info_group.add_argument(
|
||||||
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
||||||
)
|
)
|
||||||
|
info_group.add_argument(
|
||||||
|
'-P', '--prefix', help='Only show info for archive names starting with this prefix'
|
||||||
|
)
|
||||||
|
info_group.add_argument(
|
||||||
|
'-a',
|
||||||
|
'--glob-archives',
|
||||||
|
metavar='GLOB',
|
||||||
|
help='Only show info for archive names matching this glob',
|
||||||
|
)
|
||||||
|
info_group.add_argument(
|
||||||
|
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
|
||||||
|
)
|
||||||
|
info_group.add_argument(
|
||||||
|
'--first',
|
||||||
|
metavar='N',
|
||||||
|
help='Show info for first N archives after other filters are applied',
|
||||||
|
)
|
||||||
|
info_group.add_argument(
|
||||||
|
'--last', metavar='N', help='Show info for first N archives after other filters are applied'
|
||||||
|
)
|
||||||
info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
arguments = parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers)
|
arguments = parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers)
|
||||||
|
|
|
@ -178,16 +178,17 @@ def run_actions(
|
||||||
if json_output:
|
if json_output:
|
||||||
yield json.loads(json_output)
|
yield json.loads(json_output)
|
||||||
if 'info' in arguments:
|
if 'info' in arguments:
|
||||||
logger.info('{}: Displaying summary info for archives'.format(repository))
|
if arguments['info'].repository is None or repository == arguments['info'].repository:
|
||||||
json_output = borg_info.display_archives_info(
|
logger.info('{}: Displaying summary info for archives'.format(repository))
|
||||||
repository,
|
json_output = borg_info.display_archives_info(
|
||||||
storage,
|
repository,
|
||||||
local_path=local_path,
|
storage,
|
||||||
remote_path=remote_path,
|
info_arguments=arguments['info'],
|
||||||
json=arguments['info'].json,
|
local_path=local_path,
|
||||||
)
|
remote_path=remote_path,
|
||||||
if json_output:
|
)
|
||||||
yield json.loads(json_output)
|
if json_output:
|
||||||
|
yield json.loads(json_output)
|
||||||
|
|
||||||
|
|
||||||
def load_configurations(config_filenames):
|
def load_configurations(config_filenames):
|
||||||
|
|
|
@ -30,7 +30,9 @@ def test_make_flags_from_arguments_flattens_multiple_arguments():
|
||||||
)
|
)
|
||||||
arguments = flexmock(foo='bar', baz='quux')
|
arguments = flexmock(foo='bar', baz='quux')
|
||||||
|
|
||||||
assert module.make_flags_from_arguments(arguments) == ('foo', 'bar', 'baz', 'quux')
|
assert sorted(module.make_flags_from_arguments(arguments)) == sorted(
|
||||||
|
('foo', 'bar', 'baz', 'quux')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_make_flags_from_arguments_excludes_underscored_argument_names():
|
def test_make_flags_from_arguments_excludes_underscored_argument_names():
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import pytest
|
||||||
from flexmock import flexmock
|
from flexmock import flexmock
|
||||||
|
|
||||||
from borgmatic.borg import info as module
|
from borgmatic.borg import info as module
|
||||||
|
@ -14,7 +15,9 @@ def test_display_archives_info_calls_borg_with_parameters():
|
||||||
INFO_COMMAND, output_log_level=logging.WARNING
|
INFO_COMMAND, output_log_level=logging.WARNING
|
||||||
)
|
)
|
||||||
|
|
||||||
module.display_archives_info(repository='repo', storage_config={})
|
module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
|
def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
|
||||||
|
@ -22,7 +25,9 @@ def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
|
||||||
INFO_COMMAND + ('--info',), output_log_level=logging.WARNING
|
INFO_COMMAND + ('--info',), output_log_level=logging.WARNING
|
||||||
)
|
)
|
||||||
insert_logging_mock(logging.INFO)
|
insert_logging_mock(logging.INFO)
|
||||||
module.display_archives_info(repository='repo', storage_config={})
|
module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
|
def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
|
||||||
|
@ -31,7 +36,9 @@ def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_outpu
|
||||||
).and_return('[]')
|
).and_return('[]')
|
||||||
|
|
||||||
insert_logging_mock(logging.INFO)
|
insert_logging_mock(logging.INFO)
|
||||||
json_output = module.display_archives_info(repository='repo', storage_config={}, json=True)
|
json_output = module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True)
|
||||||
|
)
|
||||||
|
|
||||||
assert json_output == '[]'
|
assert json_output == '[]'
|
||||||
|
|
||||||
|
@ -42,7 +49,9 @@ def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
|
||||||
)
|
)
|
||||||
insert_logging_mock(logging.DEBUG)
|
insert_logging_mock(logging.DEBUG)
|
||||||
|
|
||||||
module.display_archives_info(repository='repo', storage_config={})
|
module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
|
def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
|
||||||
|
@ -51,7 +60,9 @@ def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_outp
|
||||||
).and_return('[]')
|
).and_return('[]')
|
||||||
|
|
||||||
insert_logging_mock(logging.DEBUG)
|
insert_logging_mock(logging.DEBUG)
|
||||||
json_output = module.display_archives_info(repository='repo', storage_config={}, json=True)
|
json_output = module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True)
|
||||||
|
)
|
||||||
|
|
||||||
assert json_output == '[]'
|
assert json_output == '[]'
|
||||||
|
|
||||||
|
@ -61,17 +72,34 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
|
||||||
INFO_COMMAND + ('--json',), output_log_level=None
|
INFO_COMMAND + ('--json',), output_log_level=None
|
||||||
).and_return('[]')
|
).and_return('[]')
|
||||||
|
|
||||||
json_output = module.display_archives_info(repository='repo', storage_config={}, json=True)
|
json_output = module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True)
|
||||||
|
)
|
||||||
|
|
||||||
assert json_output == '[]'
|
assert json_output == '[]'
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_archives_info_with_archive_calls_borg_with_archive_parameter():
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
('borg', 'info', 'repo::archive'), output_log_level=logging.WARNING
|
||||||
|
)
|
||||||
|
|
||||||
|
module.display_archives_info(
|
||||||
|
repository='repo', storage_config={}, info_arguments=flexmock(archive='archive', json=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_display_archives_info_with_local_path_calls_borg_via_local_path():
|
def test_display_archives_info_with_local_path_calls_borg_via_local_path():
|
||||||
flexmock(module).should_receive('execute_command').with_args(
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
('borg1',) + INFO_COMMAND[1:], output_log_level=logging.WARNING
|
('borg1',) + INFO_COMMAND[1:], output_log_level=logging.WARNING
|
||||||
)
|
)
|
||||||
|
|
||||||
module.display_archives_info(repository='repo', storage_config={}, local_path='borg1')
|
module.display_archives_info(
|
||||||
|
repository='repo',
|
||||||
|
storage_config={},
|
||||||
|
info_arguments=flexmock(archive=None, json=False),
|
||||||
|
local_path='borg1',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
|
def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
|
||||||
|
@ -79,7 +107,12 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
|
||||||
INFO_COMMAND + ('--remote-path', 'borg1'), output_log_level=logging.WARNING
|
INFO_COMMAND + ('--remote-path', 'borg1'), output_log_level=logging.WARNING
|
||||||
)
|
)
|
||||||
|
|
||||||
module.display_archives_info(repository='repo', storage_config={}, remote_path='borg1')
|
module.display_archives_info(
|
||||||
|
repository='repo',
|
||||||
|
storage_config={},
|
||||||
|
info_arguments=flexmock(archive=None, json=False),
|
||||||
|
remote_path='borg1',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||||
|
@ -88,4 +121,22 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
|
||||||
INFO_COMMAND + ('--lock-wait', '5'), output_log_level=logging.WARNING
|
INFO_COMMAND + ('--lock-wait', '5'), output_log_level=logging.WARNING
|
||||||
)
|
)
|
||||||
|
|
||||||
module.display_archives_info(repository='repo', storage_config=storage_config)
|
module.display_archives_info(
|
||||||
|
repository='repo',
|
||||||
|
storage_config=storage_config,
|
||||||
|
info_arguments=flexmock(archive=None, json=False),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('argument_name', ('prefix', 'glob_archives', 'sort_by', 'first', 'last'))
|
||||||
|
def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
INFO_COMMAND + ('--' + argument_name.replace('_', '-'), 'value'),
|
||||||
|
output_log_level=logging.WARNING,
|
||||||
|
)
|
||||||
|
|
||||||
|
module.display_archives_info(
|
||||||
|
repository='repo',
|
||||||
|
storage_config={},
|
||||||
|
info_arguments=flexmock(archive=None, json=False, **{argument_name: 'value'}),
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue