2022-05-28 21:42:19 +00:00
|
|
|
import argparse
|
|
|
|
import json
|
2017-11-03 05:22:40 +00:00
|
|
|
import logging
|
2017-08-05 23:21:39 +00:00
|
|
|
|
2024-03-20 18:58:59 +00:00
|
|
|
from borgmatic.borg import environment, feature, flags, rinfo
|
2020-05-10 04:53:16 +00:00
|
|
|
from borgmatic.execute import DO_NOT_CAPTURE, execute_command
|
2017-08-05 23:21:39 +00:00
|
|
|
|
2019-06-17 18:53:08 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2017-11-03 05:22:40 +00:00
|
|
|
|
|
|
|
|
2024-03-20 18:58:59 +00:00
|
|
|
def make_archive_filter_flags(local_borg_version, config, checks, check_arguments):
|
2022-05-28 21:42:19 +00:00
|
|
|
'''
|
2024-03-20 18:58:59 +00:00
|
|
|
Given the local Borg version, a configuration dict, a parsed sequence of checks, and check
|
|
|
|
arguments as an argparse.Namespace instance, transform the checks into tuple of command-line
|
|
|
|
flags for filtering archives in a check command.
|
2022-05-28 21:42:19 +00:00
|
|
|
|
2024-03-20 18:58:59 +00:00
|
|
|
If "check_last" is set in the configuration and "archives" is in checks, then include a "--last"
|
|
|
|
flag. And if "prefix" is set in configuration and "archives" is in checks, then include a
|
|
|
|
"--match-archives" flag.
|
2017-08-05 23:21:39 +00:00
|
|
|
'''
|
2024-03-20 18:58:59 +00:00
|
|
|
check_last = config.get('check_last', None)
|
|
|
|
prefix = config.get('prefix')
|
2017-08-05 23:21:39 +00:00
|
|
|
|
2023-05-16 06:17:45 +00:00
|
|
|
if 'archives' in checks or 'data' in checks:
|
|
|
|
return (('--last', str(check_last)) if check_last else ()) + (
|
2023-03-31 22:21:08 +00:00
|
|
|
(
|
|
|
|
('--match-archives', f'sh:{prefix}*')
|
|
|
|
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version)
|
|
|
|
else ('--glob-archives', f'{prefix}*')
|
|
|
|
)
|
|
|
|
if prefix
|
|
|
|
else (
|
|
|
|
flags.make_match_archives_flags(
|
2023-10-29 23:22:39 +00:00
|
|
|
check_arguments.match_archives or config.get('match_archives'),
|
2023-07-09 06:14:30 +00:00
|
|
|
config.get('archive_name_format'),
|
2023-04-02 06:57:55 +00:00
|
|
|
local_borg_version,
|
2023-03-31 22:21:08 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2023-05-16 06:17:45 +00:00
|
|
|
|
|
|
|
if check_last:
|
|
|
|
logger.warning(
|
|
|
|
'Ignoring check_last option, as "archives" or "data" are not in consistency checks'
|
|
|
|
)
|
|
|
|
if prefix:
|
|
|
|
logger.warning(
|
|
|
|
'Ignoring consistency prefix option, as "archives" or "data" are not in consistency checks'
|
|
|
|
)
|
|
|
|
|
|
|
|
return ()
|
|
|
|
|
|
|
|
|
|
|
|
def make_check_flags(checks, archive_filter_flags):
|
|
|
|
'''
|
|
|
|
Given a parsed sequence of checks and a sequence of flags to filter archives, transform the
|
|
|
|
checks into tuple of command-line check flags.
|
|
|
|
|
|
|
|
For example, given parsed checks of:
|
|
|
|
|
|
|
|
('repository',)
|
|
|
|
|
|
|
|
This will be returned as:
|
|
|
|
|
|
|
|
('--repository-only',)
|
|
|
|
|
|
|
|
However, if both "repository" and "archives" are in checks, then omit them from the returned
|
|
|
|
flags because Borg does both checks by default. If "data" is in checks, that implies "archives".
|
|
|
|
'''
|
|
|
|
if 'data' in checks:
|
|
|
|
data_flags = ('--verify-data',)
|
|
|
|
checks += ('archives',)
|
2018-05-21 05:11:40 +00:00
|
|
|
else:
|
2023-05-16 06:17:45 +00:00
|
|
|
data_flags = ()
|
2018-09-30 05:45:00 +00:00
|
|
|
|
2023-05-31 06:19:33 +00:00
|
|
|
common_flags = (archive_filter_flags if 'archives' in checks else ()) + data_flags
|
2019-09-18 23:52:27 +00:00
|
|
|
|
2022-05-28 21:42:19 +00:00
|
|
|
if {'repository', 'archives'}.issubset(set(checks)):
|
2019-09-18 23:52:27 +00:00
|
|
|
return common_flags
|
2017-08-05 23:21:39 +00:00
|
|
|
|
2018-09-30 05:45:00 +00:00
|
|
|
return (
|
2023-03-24 06:11:14 +00:00
|
|
|
tuple(f'--{check}-only' for check in checks if check in ('repository', 'archives'))
|
2019-09-18 23:52:27 +00:00
|
|
|
+ common_flags
|
2018-09-30 05:45:00 +00:00
|
|
|
)
|
2017-08-05 23:21:39 +00:00
|
|
|
|
|
|
|
|
2024-04-04 21:23:56 +00:00
|
|
|
def get_repository_id(
|
|
|
|
repository_path, config, local_borg_version, global_arguments, local_path, remote_path
|
|
|
|
):
|
2022-05-28 21:42:19 +00:00
|
|
|
'''
|
2024-03-20 18:58:59 +00:00
|
|
|
Given a local or remote repository path, a configuration dict, the local Borg version, global
|
|
|
|
arguments, and local/remote commands to run, return the corresponding Borg repository ID.
|
2023-05-16 06:17:45 +00:00
|
|
|
|
2024-03-20 18:58:59 +00:00
|
|
|
Raise ValueError if the Borg repository ID cannot be determined.
|
2022-05-28 21:42:19 +00:00
|
|
|
'''
|
|
|
|
try:
|
2024-03-20 18:58:59 +00:00
|
|
|
return json.loads(
|
|
|
|
rinfo.display_repository_info(
|
|
|
|
repository_path,
|
|
|
|
config,
|
|
|
|
local_borg_version,
|
|
|
|
argparse.Namespace(json=True),
|
|
|
|
global_arguments,
|
|
|
|
local_path,
|
|
|
|
remote_path,
|
2023-05-16 06:17:45 +00:00
|
|
|
)
|
2024-03-20 18:58:59 +00:00
|
|
|
)['repository']['id']
|
|
|
|
except (json.JSONDecodeError, KeyError):
|
|
|
|
raise ValueError(f'Cannot determine Borg repository ID for {repository_path}')
|
2023-05-16 06:17:45 +00:00
|
|
|
|
|
|
|
|
2018-09-30 05:45:00 +00:00
|
|
|
def check_archives(
|
2023-03-26 18:22:25 +00:00
|
|
|
repository_path,
|
2023-07-09 06:14:30 +00:00
|
|
|
config,
|
2022-08-12 21:53:20 +00:00
|
|
|
local_borg_version,
|
2023-10-29 23:22:39 +00:00
|
|
|
check_arguments,
|
2023-05-09 06:00:49 +00:00
|
|
|
global_arguments,
|
2024-03-20 18:58:59 +00:00
|
|
|
checks,
|
|
|
|
archive_filter_flags,
|
2019-09-19 18:43:53 +00:00
|
|
|
local_path='borg',
|
|
|
|
remote_path=None,
|
2018-09-30 05:45:00 +00:00
|
|
|
):
|
2017-08-05 23:21:39 +00:00
|
|
|
'''
|
2023-10-29 23:22:39 +00:00
|
|
|
Given a local or remote repository path, a configuration dict, the local Borg version, check
|
2024-03-20 18:58:59 +00:00
|
|
|
arguments as an argparse.Namespace instance, global arguments, a set of named Borg checks to run
|
|
|
|
(some combination "repository", "archives", and/or "data"), archive filter flags, and
|
|
|
|
local/remote commands to run, check the contained Borg archives for consistency.
|
2017-08-05 23:21:39 +00:00
|
|
|
'''
|
2024-03-20 18:58:59 +00:00
|
|
|
lock_wait = config.get('lock_wait')
|
2023-07-09 06:14:30 +00:00
|
|
|
extra_borg_options = config.get('extra_borg_options', {}).get('check', '')
|
2023-05-16 06:17:45 +00:00
|
|
|
|
2024-03-20 18:58:59 +00:00
|
|
|
verbosity_flags = ()
|
|
|
|
if logger.isEnabledFor(logging.INFO):
|
|
|
|
verbosity_flags = ('--info',)
|
|
|
|
if logger.isEnabledFor(logging.DEBUG):
|
|
|
|
verbosity_flags = ('--debug', '--show-rc')
|
|
|
|
|
|
|
|
full_command = (
|
|
|
|
(local_path, 'check')
|
|
|
|
+ (('--repair',) if check_arguments.repair else ())
|
|
|
|
+ make_check_flags(checks, archive_filter_flags)
|
|
|
|
+ (('--remote-path', remote_path) if remote_path else ())
|
|
|
|
+ (('--log-json',) if global_arguments.log_json else ())
|
|
|
|
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
|
|
|
+ verbosity_flags
|
|
|
|
+ (('--progress',) if check_arguments.progress else ())
|
|
|
|
+ (tuple(extra_borg_options.split(' ')) if extra_borg_options else ())
|
|
|
|
+ flags.make_repository_flags(repository_path, local_borg_version)
|
2022-05-28 21:42:19 +00:00
|
|
|
)
|
2017-08-05 23:21:39 +00:00
|
|
|
|
2024-03-20 18:58:59 +00:00
|
|
|
borg_environment = environment.make_environment(config)
|
|
|
|
borg_exit_codes = config.get('borg_exit_codes')
|
|
|
|
|
|
|
|
# The Borg repair option triggers an interactive prompt, which won't work when output is
|
|
|
|
# captured. And progress messes with the terminal directly.
|
|
|
|
if check_arguments.repair or check_arguments.progress:
|
|
|
|
execute_command(
|
|
|
|
full_command,
|
|
|
|
output_file=DO_NOT_CAPTURE,
|
|
|
|
extra_environment=borg_environment,
|
|
|
|
borg_local_path=local_path,
|
|
|
|
borg_exit_codes=borg_exit_codes,
|
2018-09-30 05:45:00 +00:00
|
|
|
)
|
2024-03-20 18:58:59 +00:00
|
|
|
else:
|
|
|
|
execute_command(
|
|
|
|
full_command,
|
|
|
|
extra_environment=borg_environment,
|
|
|
|
borg_local_path=local_path,
|
|
|
|
borg_exit_codes=borg_exit_codes,
|
2022-06-30 20:42:17 +00:00
|
|
|
)
|