1330 lines
52 KiB
Python
1330 lines
52 KiB
Python
import collections
|
|
import itertools
|
|
import sys
|
|
from argparse import ArgumentParser
|
|
|
|
from borgmatic.config import collect
|
|
|
|
ACTION_ALIASES = {
|
|
'rcreate': ['init', '-I'],
|
|
'prune': ['-p'],
|
|
'compact': [],
|
|
'create': ['-C'],
|
|
'check': ['-k'],
|
|
'config': [],
|
|
'extract': ['-x'],
|
|
'export-tar': [],
|
|
'mount': ['-m'],
|
|
'umount': ['-u'],
|
|
'restore': ['-r'],
|
|
'rlist': [],
|
|
'list': ['-l'],
|
|
'rinfo': [],
|
|
'info': ['-i'],
|
|
'transfer': [],
|
|
'break-lock': [],
|
|
'key': [],
|
|
'borg': [],
|
|
}
|
|
|
|
|
|
def get_subaction_parsers(action_parser):
|
|
'''
|
|
Given an argparse.ArgumentParser instance, lookup the subactions in it and return a dict from
|
|
subaction name to subaction parser.
|
|
'''
|
|
if not action_parser._subparsers:
|
|
return {}
|
|
|
|
return {
|
|
subaction_name: subaction_parser
|
|
for group_action in action_parser._subparsers._group_actions
|
|
for subaction_name, subaction_parser in group_action.choices.items()
|
|
}
|
|
|
|
|
|
def get_subactions_for_actions(action_parsers):
|
|
'''
|
|
Given a dict from action name to an argparse.ArgumentParser instance, make a map from action
|
|
name to the names of contained sub-actions.
|
|
'''
|
|
return {
|
|
action: tuple(
|
|
subaction_name
|
|
for group_action in action_parser._subparsers._group_actions
|
|
for subaction_name in group_action.choices.keys()
|
|
)
|
|
for action, action_parser in action_parsers.items()
|
|
if action_parser._subparsers
|
|
}
|
|
|
|
|
|
def omit_values_colliding_with_action_names(unparsed_arguments, parsed_arguments):
|
|
'''
|
|
Given a sequence of string arguments and a dict from action name to parsed argparse.Namespace
|
|
arguments, return the string arguments with any values omitted that happen to be the same as
|
|
the name of a borgmatic action.
|
|
|
|
This prevents, for instance, "check --only extract" from triggering the "extract" action.
|
|
'''
|
|
remaining_arguments = list(unparsed_arguments)
|
|
|
|
for action_name, parsed in parsed_arguments.items():
|
|
for value in vars(parsed).values():
|
|
if isinstance(value, str):
|
|
if value in ACTION_ALIASES.keys():
|
|
remaining_arguments.remove(value)
|
|
elif isinstance(value, list):
|
|
for item in value:
|
|
if item in ACTION_ALIASES.keys():
|
|
remaining_arguments.remove(item)
|
|
|
|
return tuple(remaining_arguments)
|
|
|
|
|
|
def parse_and_record_action_arguments(
|
|
unparsed_arguments, parsed_arguments, action_parser, action_name, canonical_name=None
|
|
):
|
|
'''
|
|
Given unparsed arguments as a sequence of strings, parsed arguments as a dict from action name
|
|
to parsed argparse.Namespace, a parser to parse with, an action name, and an optional canonical
|
|
action name (in case this the action name is an alias), parse the arguments and return a list of
|
|
any remaining string arguments that were not parsed. Also record the parsed argparse.Namespace
|
|
by setting it into the given parsed arguments. Return None if no parsing occurs because the
|
|
given action doesn't apply to the given unparsed arguments.
|
|
'''
|
|
filtered_arguments = omit_values_colliding_with_action_names(
|
|
unparsed_arguments, parsed_arguments
|
|
)
|
|
|
|
if action_name not in filtered_arguments:
|
|
return tuple(unparsed_arguments)
|
|
|
|
parsed, remaining = action_parser.parse_known_args(filtered_arguments)
|
|
parsed_arguments[canonical_name or action_name] = parsed
|
|
|
|
# Special case: If this is a "borg" action, greedily consume all arguments after (+1) the "borg"
|
|
# argument.
|
|
if action_name == 'borg':
|
|
borg_options_index = remaining.index('borg') + 1
|
|
parsed_arguments['borg'].options = remaining[borg_options_index:]
|
|
remaining = remaining[:borg_options_index]
|
|
|
|
return tuple(argument for argument in remaining if argument != action_name)
|
|
|
|
|
|
def get_unparsable_arguments(remaining_action_arguments):
|
|
'''
|
|
Given a sequence of argument tuples (one per action parser that parsed arguments), determine the
|
|
remaining arguments that no action parsers have consumed.
|
|
'''
|
|
if not remaining_action_arguments:
|
|
return ()
|
|
|
|
return tuple(
|
|
argument
|
|
for argument in dict.fromkeys(
|
|
itertools.chain.from_iterable(remaining_action_arguments)
|
|
).keys()
|
|
if all(argument in action_arguments for action_arguments in remaining_action_arguments)
|
|
)
|
|
|
|
|
|
def parse_arguments_for_actions(unparsed_arguments, action_parsers, global_parser):
|
|
'''
|
|
Given a sequence of arguments, a dict from action name to argparse.ArgumentParser instance,
|
|
and the global parser as a argparse.ArgumentParser instance, give each requested action's
|
|
parser a shot at parsing all arguments. This allows common arguments like "--repository" to be
|
|
shared across multiple action parsers.
|
|
|
|
Return the result as a tuple of: (a dict mapping from action name to an argparse.Namespace of
|
|
parsed arguments, a tuple of argument tuples where each is the remaining arguments not claimed
|
|
by any action parser).
|
|
'''
|
|
arguments = collections.OrderedDict()
|
|
help_requested = bool('--help' in unparsed_arguments or '-h' in unparsed_arguments)
|
|
remaining_action_arguments = []
|
|
alias_to_action_name = {
|
|
alias: action_name for action_name, aliases in ACTION_ALIASES.items() for alias in aliases
|
|
}
|
|
|
|
# If the "borg" action is used, skip all other action parsers. This avoids confusion like
|
|
# "borg list" triggering borgmatic's own list action.
|
|
if 'borg' in unparsed_arguments:
|
|
action_parsers = {'borg': action_parsers['borg']}
|
|
|
|
# Ask each action parser, one by one, to parse arguments.
|
|
for argument in unparsed_arguments:
|
|
action_name = argument
|
|
canonical_name = alias_to_action_name.get(action_name, action_name)
|
|
action_parser = action_parsers.get(action_name)
|
|
|
|
if not action_parser:
|
|
continue
|
|
|
|
subaction_parsers = get_subaction_parsers(action_parser)
|
|
|
|
# But first parse with subaction parsers, if any.
|
|
if subaction_parsers:
|
|
subactions_parsed = False
|
|
|
|
for subaction_name, subaction_parser in subaction_parsers.items():
|
|
remaining_action_arguments.append(
|
|
tuple(
|
|
argument
|
|
for argument in parse_and_record_action_arguments(
|
|
unparsed_arguments,
|
|
arguments,
|
|
subaction_parser,
|
|
subaction_name,
|
|
)
|
|
if argument != action_name
|
|
)
|
|
)
|
|
|
|
if subaction_name in arguments:
|
|
subactions_parsed = True
|
|
|
|
if not subactions_parsed:
|
|
if help_requested:
|
|
action_parser.print_help()
|
|
sys.exit(0)
|
|
else:
|
|
raise ValueError(
|
|
f"Missing sub-action after {action_name} action. Expected one of: {', '.join(get_subactions_for_actions(action_parsers)[action_name])}"
|
|
)
|
|
# Otherwise, parse with the main action parser.
|
|
else:
|
|
remaining_action_arguments.append(
|
|
parse_and_record_action_arguments(
|
|
unparsed_arguments, arguments, action_parser, action_name, canonical_name
|
|
)
|
|
)
|
|
|
|
# If no actions were explicitly requested, assume defaults.
|
|
if not arguments and not help_requested:
|
|
for default_action_name in ('create', 'prune', 'compact', 'check'):
|
|
default_action_parser = action_parsers[default_action_name]
|
|
remaining_action_arguments.append(
|
|
parse_and_record_action_arguments(
|
|
tuple(unparsed_arguments) + (default_action_name,),
|
|
arguments,
|
|
default_action_parser,
|
|
default_action_name,
|
|
)
|
|
)
|
|
|
|
arguments['global'], remaining = global_parser.parse_known_args(unparsed_arguments)
|
|
remaining_action_arguments.append(remaining)
|
|
|
|
return (
|
|
arguments,
|
|
tuple(remaining_action_arguments) if arguments else unparsed_arguments,
|
|
)
|
|
|
|
|
|
def make_parsers():
|
|
'''
|
|
Build a global arguments parser, individual action parsers, and a combined parser containing
|
|
both. Return them as a tuple. The global parser is useful for parsing just global arguments
|
|
while ignoring actions, and the combined parser is handy for displaying help that includes
|
|
everything: global flags, a list of actions, etc.
|
|
'''
|
|
config_paths = collect.get_default_config_paths(expand_home=True)
|
|
unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
|
|
|
|
global_parser = ArgumentParser(add_help=False)
|
|
global_group = global_parser.add_argument_group('global arguments')
|
|
|
|
global_group.add_argument(
|
|
'-c',
|
|
'--config',
|
|
dest='config_paths',
|
|
action='append',
|
|
help=f"Configuration filename or directory, can specify flag multiple times, defaults to: {' '.join(unexpanded_config_paths)}",
|
|
)
|
|
global_group.add_argument(
|
|
'-n',
|
|
'--dry-run',
|
|
dest='dry_run',
|
|
action='store_true',
|
|
help='Go through the motions, but do not actually write to any repositories',
|
|
)
|
|
global_group.add_argument(
|
|
'-nc', '--no-color', dest='no_color', action='store_true', help='Disable colored output'
|
|
)
|
|
global_group.add_argument(
|
|
'-v',
|
|
'--verbosity',
|
|
type=int,
|
|
choices=range(-2, 3),
|
|
default=0,
|
|
help='Display verbose progress to the console (disabled, errors only, default, some, or lots: -2, -1, 0, 1, or 2)',
|
|
)
|
|
global_group.add_argument(
|
|
'--syslog-verbosity',
|
|
type=int,
|
|
choices=range(-2, 3),
|
|
default=0,
|
|
help='Log verbose progress to syslog (disabled, errors only, default, some, or lots: -2, -1, 0, 1, or 2). Ignored when console is interactive or --log-file is given',
|
|
)
|
|
global_group.add_argument(
|
|
'--log-file-verbosity',
|
|
type=int,
|
|
choices=range(-2, 3),
|
|
default=0,
|
|
help='Log verbose progress to log file (disabled, errors only, default, some, or lots: -2, -1, 0, 1, or 2). Only used when --log-file is given',
|
|
)
|
|
global_group.add_argument(
|
|
'--monitoring-verbosity',
|
|
type=int,
|
|
choices=range(-2, 3),
|
|
default=0,
|
|
help='Log verbose progress to monitoring integrations that support logging (from disabled, errors only, default, some, or lots: -2, -1, 0, 1, or 2)',
|
|
)
|
|
global_group.add_argument(
|
|
'--log-file',
|
|
type=str,
|
|
help='Write log messages to this file instead of syslog',
|
|
)
|
|
global_group.add_argument(
|
|
'--log-file-format',
|
|
type=str,
|
|
help='Log format string used for log messages written to the log file',
|
|
)
|
|
global_group.add_argument(
|
|
'--log-json',
|
|
action='store_true',
|
|
help='Write log messages and console output as one JSON object per log line instead of formatted text',
|
|
)
|
|
global_group.add_argument(
|
|
'--override',
|
|
metavar='OPTION.SUBOPTION=VALUE',
|
|
dest='overrides',
|
|
action='append',
|
|
help='Configuration file option to override with specified value, can specify flag multiple times',
|
|
)
|
|
global_group.add_argument(
|
|
'--no-environment-interpolation',
|
|
dest='resolve_env',
|
|
action='store_false',
|
|
help='Do not resolve environment variables in configuration file',
|
|
)
|
|
global_group.add_argument(
|
|
'--bash-completion',
|
|
default=False,
|
|
action='store_true',
|
|
help='Show bash completion script and exit',
|
|
)
|
|
global_group.add_argument(
|
|
'--fish-completion',
|
|
default=False,
|
|
action='store_true',
|
|
help='Show fish completion script and exit',
|
|
)
|
|
global_group.add_argument(
|
|
'--version',
|
|
dest='version',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display installed version number of borgmatic and exit',
|
|
)
|
|
|
|
global_plus_action_parser = ArgumentParser(
|
|
description='''
|
|
Simple, configuration-driven backup software for servers and workstations. If no actions
|
|
are given, then borgmatic defaults to: create, prune, compact, and check.
|
|
''',
|
|
parents=[global_parser],
|
|
)
|
|
|
|
action_parsers = global_plus_action_parser.add_subparsers(
|
|
title='actions',
|
|
metavar='',
|
|
help='Specify zero or more actions. Defaults to create, prune, compact, and check. Use --help with action for details:',
|
|
)
|
|
rcreate_parser = action_parsers.add_parser(
|
|
'rcreate',
|
|
aliases=ACTION_ALIASES['rcreate'],
|
|
help='Create a new, empty Borg repository',
|
|
description='Create a new, empty Borg repository',
|
|
add_help=False,
|
|
)
|
|
rcreate_group = rcreate_parser.add_argument_group('rcreate arguments')
|
|
rcreate_group.add_argument(
|
|
'-e',
|
|
'--encryption',
|
|
dest='encryption_mode',
|
|
help='Borg repository encryption mode',
|
|
required=True,
|
|
)
|
|
rcreate_group.add_argument(
|
|
'--source-repository',
|
|
'--other-repo',
|
|
metavar='KEY_REPOSITORY',
|
|
help='Path to an existing Borg repository whose key material should be reused [Borg 2.x+ only]',
|
|
)
|
|
rcreate_group.add_argument(
|
|
'--repository',
|
|
help='Path of the new repository to create (must be already specified in a borgmatic configuration file), defaults to the configured repository if there is only one',
|
|
)
|
|
rcreate_group.add_argument(
|
|
'--copy-crypt-key',
|
|
action='store_true',
|
|
help='Copy the crypt key used for authenticated encryption from the source repository, defaults to a new random key [Borg 2.x+ only]',
|
|
)
|
|
rcreate_group.add_argument(
|
|
'--append-only',
|
|
action='store_true',
|
|
help='Create an append-only repository',
|
|
)
|
|
rcreate_group.add_argument(
|
|
'--storage-quota',
|
|
help='Create a repository with a fixed storage quota',
|
|
)
|
|
rcreate_group.add_argument(
|
|
'--make-parent-dirs',
|
|
action='store_true',
|
|
help='Create any missing parent directories of the repository directory',
|
|
)
|
|
rcreate_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
transfer_parser = action_parsers.add_parser(
|
|
'transfer',
|
|
aliases=ACTION_ALIASES['transfer'],
|
|
help='Transfer archives from one repository to another, optionally upgrading the transferred data [Borg 2.0+ only]',
|
|
description='Transfer archives from one repository to another, optionally upgrading the transferred data [Borg 2.0+ only]',
|
|
add_help=False,
|
|
)
|
|
transfer_group = transfer_parser.add_argument_group('transfer arguments')
|
|
transfer_group.add_argument(
|
|
'--repository',
|
|
help='Path of existing destination repository to transfer archives to, defaults to the configured repository if there is only one',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--source-repository',
|
|
help='Path of existing source repository to transfer archives from',
|
|
required=True,
|
|
)
|
|
transfer_group.add_argument(
|
|
'--archive',
|
|
help='Name of single archive to transfer (or "latest"), defaults to transferring all archives',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--upgrader',
|
|
help='Upgrader type used to convert the transferred data, e.g. "From12To20" to upgrade data from Borg 1.2 to 2.0 format, defaults to no conversion',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--progress',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display progress as each archive is transferred',
|
|
)
|
|
transfer_group.add_argument(
|
|
'-a',
|
|
'--match-archives',
|
|
'--glob-archives',
|
|
metavar='PATTERN',
|
|
help='Only transfer archives with names matching this pattern',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
|
|
)
|
|
transfer_group.add_argument(
|
|
'--first',
|
|
metavar='N',
|
|
help='Only transfer first N archives after other filters are applied',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--last', metavar='N', help='Only transfer last N archives after other filters are applied'
|
|
)
|
|
transfer_group.add_argument(
|
|
'--oldest',
|
|
metavar='TIMESPAN',
|
|
help='Transfer archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--newest',
|
|
metavar='TIMESPAN',
|
|
help='Transfer archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--older',
|
|
metavar='TIMESPAN',
|
|
help='Transfer archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
transfer_group.add_argument(
|
|
'--newer',
|
|
metavar='TIMESPAN',
|
|
help='Transfer archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
transfer_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
prune_parser = action_parsers.add_parser(
|
|
'prune',
|
|
aliases=ACTION_ALIASES['prune'],
|
|
help='Prune archives according to the retention policy (with Borg 1.2+, run compact afterwards to actually free space)',
|
|
description='Prune archives according to the retention policy (with Borg 1.2+, run compact afterwards to actually free space)',
|
|
add_help=False,
|
|
)
|
|
prune_group = prune_parser.add_argument_group('prune arguments')
|
|
prune_group.add_argument(
|
|
'--repository',
|
|
help='Path of specific existing repository to prune (must be already specified in a borgmatic configuration file)',
|
|
)
|
|
prune_group.add_argument(
|
|
'--stats',
|
|
dest='stats',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display statistics of archive',
|
|
)
|
|
prune_group.add_argument(
|
|
'--list', dest='list_archives', action='store_true', help='List archives kept/pruned'
|
|
)
|
|
prune_group.add_argument(
|
|
'--oldest',
|
|
metavar='TIMESPAN',
|
|
help='Prune archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
prune_group.add_argument(
|
|
'--newest',
|
|
metavar='TIMESPAN',
|
|
help='Prune archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
prune_group.add_argument(
|
|
'--older',
|
|
metavar='TIMESPAN',
|
|
help='Prune archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
prune_group.add_argument(
|
|
'--newer',
|
|
metavar='TIMESPAN',
|
|
help='Prune archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
prune_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
compact_parser = action_parsers.add_parser(
|
|
'compact',
|
|
aliases=ACTION_ALIASES['compact'],
|
|
help='Compact segments to free space [Borg 1.2+, borgmatic 1.5.23+ only]',
|
|
description='Compact segments to free space [Borg 1.2+, borgmatic 1.5.23+ only]',
|
|
add_help=False,
|
|
)
|
|
compact_group = compact_parser.add_argument_group('compact arguments')
|
|
compact_group.add_argument(
|
|
'--repository',
|
|
help='Path of specific existing repository to compact (must be already specified in a borgmatic configuration file)',
|
|
)
|
|
compact_group.add_argument(
|
|
'--progress',
|
|
dest='progress',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display progress as each segment is compacted',
|
|
)
|
|
compact_group.add_argument(
|
|
'--cleanup-commits',
|
|
dest='cleanup_commits',
|
|
default=False,
|
|
action='store_true',
|
|
help='Cleanup commit-only 17-byte segment files left behind by Borg 1.1 [flag in Borg 1.2 only]',
|
|
)
|
|
compact_group.add_argument(
|
|
'--threshold',
|
|
type=int,
|
|
dest='threshold',
|
|
help='Minimum saved space percentage threshold for compacting a segment, defaults to 10',
|
|
)
|
|
compact_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
create_parser = action_parsers.add_parser(
|
|
'create',
|
|
aliases=ACTION_ALIASES['create'],
|
|
help='Create an archive (actually perform a backup)',
|
|
description='Create an archive (actually perform a backup)',
|
|
add_help=False,
|
|
)
|
|
create_group = create_parser.add_argument_group('create arguments')
|
|
create_group.add_argument(
|
|
'--repository',
|
|
help='Path of specific existing repository to backup to (must be already specified in a borgmatic configuration file)',
|
|
)
|
|
create_group.add_argument(
|
|
'--progress',
|
|
dest='progress',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display progress for each file as it is backed up',
|
|
)
|
|
create_group.add_argument(
|
|
'--stats',
|
|
dest='stats',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display statistics of archive',
|
|
)
|
|
create_group.add_argument(
|
|
'--list', '--files', dest='list_files', action='store_true', help='Show per-file details'
|
|
)
|
|
create_group.add_argument(
|
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
|
)
|
|
create_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
check_parser = action_parsers.add_parser(
|
|
'check',
|
|
aliases=ACTION_ALIASES['check'],
|
|
help='Check archives for consistency',
|
|
description='Check archives for consistency',
|
|
add_help=False,
|
|
)
|
|
check_group = check_parser.add_argument_group('check arguments')
|
|
check_group.add_argument(
|
|
'--repository',
|
|
help='Path of specific existing repository to check (must be already specified in a borgmatic configuration file)',
|
|
)
|
|
check_group.add_argument(
|
|
'--progress',
|
|
dest='progress',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display progress for each file as it is checked',
|
|
)
|
|
check_group.add_argument(
|
|
'--repair',
|
|
dest='repair',
|
|
default=False,
|
|
action='store_true',
|
|
help='Attempt to repair any inconsistencies found (for interactive use)',
|
|
)
|
|
check_group.add_argument(
|
|
'--only',
|
|
metavar='CHECK',
|
|
choices=('repository', 'archives', 'data', 'extract'),
|
|
dest='only',
|
|
action='append',
|
|
help='Run a particular consistency check (repository, archives, data, or extract) instead of configured checks (subject to configured frequency, can specify flag multiple times)',
|
|
)
|
|
check_group.add_argument(
|
|
'--force',
|
|
default=False,
|
|
action='store_true',
|
|
help='Ignore configured check frequencies and run checks unconditionally',
|
|
)
|
|
check_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
extract_parser = action_parsers.add_parser(
|
|
'extract',
|
|
aliases=ACTION_ALIASES['extract'],
|
|
help='Extract files from a named archive to the current directory',
|
|
description='Extract a named archive to the current directory',
|
|
add_help=False,
|
|
)
|
|
extract_group = extract_parser.add_argument_group('extract arguments')
|
|
extract_group.add_argument(
|
|
'--repository',
|
|
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 extract (or "latest")', required=True
|
|
)
|
|
extract_group.add_argument(
|
|
'--path',
|
|
'--restore-path',
|
|
metavar='PATH',
|
|
dest='paths',
|
|
action='append',
|
|
help='Path to extract from archive, can specify flag multiple times, defaults to the entire archive',
|
|
)
|
|
extract_group.add_argument(
|
|
'--destination',
|
|
metavar='PATH',
|
|
dest='destination',
|
|
help='Directory to extract files into, defaults to the current directory',
|
|
)
|
|
extract_group.add_argument(
|
|
'--strip-components',
|
|
type=lambda number: number if number == 'all' else int(number),
|
|
metavar='NUMBER',
|
|
help='Number of leading path components to remove from each extracted path or "all" to strip all leading path components. Skip paths with fewer elements',
|
|
)
|
|
extract_group.add_argument(
|
|
'--progress',
|
|
dest='progress',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display progress for each file as it is extracted',
|
|
)
|
|
extract_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
config_parser = action_parsers.add_parser(
|
|
'config',
|
|
aliases=ACTION_ALIASES['config'],
|
|
help='Perform configuration file related operations',
|
|
description='Perform configuration file related operations',
|
|
add_help=False,
|
|
)
|
|
|
|
config_group = config_parser.add_argument_group('config arguments')
|
|
config_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
config_parsers = config_parser.add_subparsers(
|
|
title='config sub-actions',
|
|
)
|
|
|
|
config_bootstrap_parser = config_parsers.add_parser(
|
|
'bootstrap',
|
|
help='Extract the borgmatic configuration files from a named archive',
|
|
description='Extract the borgmatic configuration files from a named archive',
|
|
add_help=False,
|
|
)
|
|
config_bootstrap_group = config_bootstrap_parser.add_argument_group(
|
|
'config bootstrap arguments'
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to extract config files from',
|
|
required=True,
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'--borgmatic-source-directory',
|
|
help='Path that stores the config files used to create an archive and additional source files used for temporary internal state like borgmatic database dumps. Defaults to ~/.borgmatic',
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'--archive',
|
|
help='Name of archive to extract config files from, defaults to "latest"',
|
|
default='latest',
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'--destination',
|
|
metavar='PATH',
|
|
dest='destination',
|
|
help='Directory to extract config files into, defaults to /',
|
|
default='/',
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'--strip-components',
|
|
type=lambda number: number if number == 'all' else int(number),
|
|
metavar='NUMBER',
|
|
help='Number of leading path components to remove from each extracted path or "all" to strip all leading path components. Skip paths with fewer elements',
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'--progress',
|
|
dest='progress',
|
|
default=False,
|
|
action='store_true',
|
|
help='Display progress for each file as it is extracted',
|
|
)
|
|
config_bootstrap_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
config_generate_parser = config_parsers.add_parser(
|
|
'generate',
|
|
help='Generate a sample borgmatic configuration file',
|
|
description='Generate a sample borgmatic configuration file',
|
|
add_help=False,
|
|
)
|
|
config_generate_group = config_generate_parser.add_argument_group('config generate arguments')
|
|
config_generate_group.add_argument(
|
|
'-s',
|
|
'--source',
|
|
dest='source_filename',
|
|
help='Optional configuration file to merge into the generated configuration, useful for upgrading your configuration',
|
|
)
|
|
config_generate_group.add_argument(
|
|
'-d',
|
|
'--destination',
|
|
dest='destination_filename',
|
|
default=config_paths[0],
|
|
help=f'Destination configuration file, default: {unexpanded_config_paths[0]}',
|
|
)
|
|
config_generate_group.add_argument(
|
|
'--overwrite',
|
|
default=False,
|
|
action='store_true',
|
|
help='Whether to overwrite any existing destination file, defaults to false',
|
|
)
|
|
config_generate_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
config_validate_parser = config_parsers.add_parser(
|
|
'validate',
|
|
help='Validate borgmatic configuration files specified with --config (see borgmatic --help)',
|
|
description='Validate borgmatic configuration files specified with --config (see borgmatic --help)',
|
|
add_help=False,
|
|
)
|
|
config_validate_group = config_validate_parser.add_argument_group('config validate arguments')
|
|
config_validate_group.add_argument(
|
|
'-s',
|
|
'--show',
|
|
action='store_true',
|
|
help='Show the validated configuration after all include merging has occurred',
|
|
)
|
|
config_validate_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
export_tar_parser = action_parsers.add_parser(
|
|
'export-tar',
|
|
aliases=ACTION_ALIASES['export-tar'],
|
|
help='Export an archive to a tar-formatted file or stream',
|
|
description='Export an archive to a tar-formatted file or stream',
|
|
add_help=False,
|
|
)
|
|
export_tar_group = export_tar_parser.add_argument_group('export-tar arguments')
|
|
export_tar_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to export from, defaults to the configured repository if there is only one',
|
|
)
|
|
export_tar_group.add_argument(
|
|
'--archive', help='Name of archive to export (or "latest")', required=True
|
|
)
|
|
export_tar_group.add_argument(
|
|
'--path',
|
|
metavar='PATH',
|
|
dest='paths',
|
|
action='append',
|
|
help='Path to export from archive, can specify flag multiple times, defaults to the entire archive',
|
|
)
|
|
export_tar_group.add_argument(
|
|
'--destination',
|
|
metavar='PATH',
|
|
dest='destination',
|
|
help='Path to destination export tar file, or "-" for stdout (but be careful about dirtying output with --verbosity or --list)',
|
|
required=True,
|
|
)
|
|
export_tar_group.add_argument(
|
|
'--tar-filter', help='Name of filter program to pipe data through'
|
|
)
|
|
export_tar_group.add_argument(
|
|
'--list', '--files', dest='list_files', action='store_true', help='Show per-file details'
|
|
)
|
|
export_tar_group.add_argument(
|
|
'--strip-components',
|
|
type=int,
|
|
metavar='NUMBER',
|
|
dest='strip_components',
|
|
help='Number of leading path components to remove from each exported path. Skip paths with fewer elements',
|
|
)
|
|
export_tar_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
mount_parser = action_parsers.add_parser(
|
|
'mount',
|
|
aliases=ACTION_ALIASES['mount'],
|
|
help='Mount files from a named archive as a FUSE filesystem',
|
|
description='Mount a named archive as a FUSE filesystem',
|
|
add_help=False,
|
|
)
|
|
mount_group = mount_parser.add_argument_group('mount arguments')
|
|
mount_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to use, defaults to the configured repository if there is only one',
|
|
)
|
|
mount_group.add_argument('--archive', help='Name of archive to mount (or "latest")')
|
|
mount_group.add_argument(
|
|
'--mount-point',
|
|
metavar='PATH',
|
|
dest='mount_point',
|
|
help='Path where filesystem is to be mounted',
|
|
required=True,
|
|
)
|
|
mount_group.add_argument(
|
|
'--path',
|
|
metavar='PATH',
|
|
dest='paths',
|
|
action='append',
|
|
help='Path to mount from archive, can specify multiple times, defaults to the entire archive',
|
|
)
|
|
mount_group.add_argument(
|
|
'--foreground',
|
|
dest='foreground',
|
|
default=False,
|
|
action='store_true',
|
|
help='Stay in foreground until ctrl-C is pressed',
|
|
)
|
|
mount_group.add_argument(
|
|
'--first',
|
|
metavar='N',
|
|
help='Mount first N archives after other filters are applied',
|
|
)
|
|
mount_group.add_argument(
|
|
'--last', metavar='N', help='Mount last N archives after other filters are applied'
|
|
)
|
|
mount_group.add_argument(
|
|
'--oldest',
|
|
metavar='TIMESPAN',
|
|
help='Mount archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
mount_group.add_argument(
|
|
'--newest',
|
|
metavar='TIMESPAN',
|
|
help='Mount archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
mount_group.add_argument(
|
|
'--older',
|
|
metavar='TIMESPAN',
|
|
help='Mount archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
mount_group.add_argument(
|
|
'--newer',
|
|
metavar='TIMESPAN',
|
|
help='Mount archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
mount_group.add_argument('--options', dest='options', help='Extra Borg mount options')
|
|
mount_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
umount_parser = action_parsers.add_parser(
|
|
'umount',
|
|
aliases=ACTION_ALIASES['umount'],
|
|
help='Unmount a FUSE filesystem that was mounted with "borgmatic mount"',
|
|
description='Unmount a mounted FUSE filesystem',
|
|
add_help=False,
|
|
)
|
|
umount_group = umount_parser.add_argument_group('umount arguments')
|
|
umount_group.add_argument(
|
|
'--mount-point',
|
|
metavar='PATH',
|
|
dest='mount_point',
|
|
help='Path of filesystem to unmount',
|
|
required=True,
|
|
)
|
|
umount_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
restore_parser = action_parsers.add_parser(
|
|
'restore',
|
|
aliases=ACTION_ALIASES['restore'],
|
|
help='Restore database dumps from a named archive',
|
|
description='Restore database dumps from a named archive. (To extract files instead, use "borgmatic extract".)',
|
|
add_help=False,
|
|
)
|
|
restore_group = restore_parser.add_argument_group('restore arguments')
|
|
restore_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to restore from, defaults to the configured repository if there is only one',
|
|
)
|
|
restore_group.add_argument(
|
|
'--archive', help='Name of archive to restore from (or "latest")', required=True
|
|
)
|
|
restore_group.add_argument(
|
|
'--database',
|
|
metavar='NAME',
|
|
dest='databases',
|
|
action='append',
|
|
help="Name of database to restore from archive, must be defined in borgmatic's configuration, can specify flag multiple times, defaults to all databases",
|
|
)
|
|
restore_group.add_argument(
|
|
'--schema',
|
|
metavar='NAME',
|
|
dest='schemas',
|
|
action='append',
|
|
help='Name of schema to restore from the database, can specify flag multiple times, defaults to all schemas. Schemas are only supported for PostgreSQL and MongoDB databases',
|
|
)
|
|
restore_group.add_argument(
|
|
'--hostname',
|
|
help='Database hostname to restore to. Defaults to the "restore_hostname" option in borgmatic\'s configuration',
|
|
)
|
|
restore_group.add_argument(
|
|
'--port',
|
|
help='Port to restore to. Defaults to the "restore_port" option in borgmatic\'s configuration',
|
|
)
|
|
restore_group.add_argument(
|
|
'--username',
|
|
help='Username with which to connect to the database. Defaults to the "restore_username" option in borgmatic\'s configuration',
|
|
)
|
|
restore_group.add_argument(
|
|
'--password',
|
|
help='Password with which to connect to the restore database. Defaults to the "restore_password" option in borgmatic\'s configuration',
|
|
)
|
|
restore_group.add_argument(
|
|
'--restore-path',
|
|
help='Path to restore SQLite database dumps to. Defaults to the "restore_path" option in borgmatic\'s configuration',
|
|
)
|
|
restore_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
rlist_parser = action_parsers.add_parser(
|
|
'rlist',
|
|
aliases=ACTION_ALIASES['rlist'],
|
|
help='List repository',
|
|
description='List the archives in a repository',
|
|
add_help=False,
|
|
)
|
|
rlist_group = rlist_parser.add_argument_group('rlist arguments')
|
|
rlist_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to list, defaults to the configured repositories',
|
|
)
|
|
rlist_group.add_argument(
|
|
'--short', default=False, action='store_true', help='Output only archive names'
|
|
)
|
|
rlist_group.add_argument('--format', help='Format for archive listing')
|
|
rlist_group.add_argument(
|
|
'--json', default=False, action='store_true', help='Output results as JSON'
|
|
)
|
|
rlist_group.add_argument(
|
|
'-P', '--prefix', help='Deprecated. Only list archive names starting with this prefix'
|
|
)
|
|
rlist_group.add_argument(
|
|
'-a',
|
|
'--match-archives',
|
|
'--glob-archives',
|
|
metavar='PATTERN',
|
|
help='Only list archive names matching this pattern',
|
|
)
|
|
rlist_group.add_argument(
|
|
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
|
|
)
|
|
rlist_group.add_argument(
|
|
'--first', metavar='N', help='List first N archives after other filters are applied'
|
|
)
|
|
rlist_group.add_argument(
|
|
'--last', metavar='N', help='List last N archives after other filters are applied'
|
|
)
|
|
rlist_group.add_argument(
|
|
'--oldest',
|
|
metavar='TIMESPAN',
|
|
help='List archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
rlist_group.add_argument(
|
|
'--newest',
|
|
metavar='TIMESPAN',
|
|
help='List archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
rlist_group.add_argument(
|
|
'--older',
|
|
metavar='TIMESPAN',
|
|
help='List archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
rlist_group.add_argument(
|
|
'--newer',
|
|
metavar='TIMESPAN',
|
|
help='List archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
rlist_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
list_parser = action_parsers.add_parser(
|
|
'list',
|
|
aliases=ACTION_ALIASES['list'],
|
|
help='List archive',
|
|
description='List the files in an archive or search for a file across archives',
|
|
add_help=False,
|
|
)
|
|
list_group = list_parser.add_argument_group('list arguments')
|
|
list_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository containing archive to list, defaults to the configured repositories',
|
|
)
|
|
list_group.add_argument('--archive', help='Name of the archive to list (or "latest")')
|
|
list_group.add_argument(
|
|
'--path',
|
|
metavar='PATH',
|
|
dest='paths',
|
|
action='append',
|
|
help='Path or pattern to list from a single selected archive (via "--archive"), can specify flag multiple times, defaults to listing the entire archive',
|
|
)
|
|
list_group.add_argument(
|
|
'--find',
|
|
metavar='PATH',
|
|
dest='find_paths',
|
|
action='append',
|
|
help='Partial path or pattern to search for and list across multiple archives, can specify flag multiple times',
|
|
)
|
|
list_group.add_argument(
|
|
'--short', default=False, action='store_true', help='Output only path names'
|
|
)
|
|
list_group.add_argument('--format', help='Format for file listing')
|
|
list_group.add_argument(
|
|
'--json', default=False, action='store_true', help='Output results as JSON'
|
|
)
|
|
list_group.add_argument(
|
|
'-P', '--prefix', help='Deprecated. Only list archive names starting with this prefix'
|
|
)
|
|
list_group.add_argument(
|
|
'-a',
|
|
'--match-archives',
|
|
'--glob-archives',
|
|
metavar='PATTERN',
|
|
help='Only list archive names matching this pattern',
|
|
)
|
|
list_group.add_argument(
|
|
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
|
|
)
|
|
list_group.add_argument(
|
|
'--first', metavar='N', help='List first N archives after other filters are applied'
|
|
)
|
|
list_group.add_argument(
|
|
'--last', metavar='N', help='List last N archives after other filters are applied'
|
|
)
|
|
list_group.add_argument(
|
|
'-e', '--exclude', metavar='PATTERN', help='Exclude paths matching the pattern'
|
|
)
|
|
list_group.add_argument(
|
|
'--exclude-from', metavar='FILENAME', help='Exclude paths from exclude file, one per line'
|
|
)
|
|
list_group.add_argument('--pattern', help='Include or exclude paths matching a pattern')
|
|
list_group.add_argument(
|
|
'--patterns-from',
|
|
metavar='FILENAME',
|
|
help='Include or exclude paths matching patterns from pattern file, one per line',
|
|
)
|
|
list_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
rinfo_parser = action_parsers.add_parser(
|
|
'rinfo',
|
|
aliases=ACTION_ALIASES['rinfo'],
|
|
help='Show repository summary information such as disk space used',
|
|
description='Show repository summary information such as disk space used',
|
|
add_help=False,
|
|
)
|
|
rinfo_group = rinfo_parser.add_argument_group('rinfo arguments')
|
|
rinfo_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to show info for, defaults to the configured repository if there is only one',
|
|
)
|
|
rinfo_group.add_argument(
|
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
|
)
|
|
rinfo_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
info_parser = action_parsers.add_parser(
|
|
'info',
|
|
aliases=ACTION_ALIASES['info'],
|
|
help='Show archive summary information such as disk space used',
|
|
description='Show archive summary information such as disk space used',
|
|
add_help=False,
|
|
)
|
|
info_group = info_parser.add_argument_group('info arguments')
|
|
info_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository containing archive 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 (or "latest")')
|
|
info_group.add_argument(
|
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
|
)
|
|
info_group.add_argument(
|
|
'-P',
|
|
'--prefix',
|
|
help='Deprecated. Only show info for archive names starting with this prefix',
|
|
)
|
|
info_group.add_argument(
|
|
'-a',
|
|
'--match-archives',
|
|
'--glob-archives',
|
|
metavar='PATTERN',
|
|
help='Only show info for archive names matching this pattern',
|
|
)
|
|
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 last N archives after other filters are applied'
|
|
)
|
|
info_group.add_argument(
|
|
'--oldest',
|
|
metavar='TIMESPAN',
|
|
help='Show info for archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
info_group.add_argument(
|
|
'--newest',
|
|
metavar='TIMESPAN',
|
|
help='Show info for archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]',
|
|
)
|
|
info_group.add_argument(
|
|
'--older',
|
|
metavar='TIMESPAN',
|
|
help='Show info for archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
info_group.add_argument(
|
|
'--newer',
|
|
metavar='TIMESPAN',
|
|
help='Show info for archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]',
|
|
)
|
|
info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
break_lock_parser = action_parsers.add_parser(
|
|
'break-lock',
|
|
aliases=ACTION_ALIASES['break-lock'],
|
|
help='Break the repository and cache locks left behind by Borg aborting',
|
|
description='Break Borg repository and cache locks left behind by Borg aborting',
|
|
add_help=False,
|
|
)
|
|
break_lock_group = break_lock_parser.add_argument_group('break-lock arguments')
|
|
break_lock_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to break the lock for, defaults to the configured repository if there is only one',
|
|
)
|
|
break_lock_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
key_parser = action_parsers.add_parser(
|
|
'key',
|
|
aliases=ACTION_ALIASES['key'],
|
|
help='Perform repository key related operations',
|
|
description='Perform repository key related operations',
|
|
add_help=False,
|
|
)
|
|
|
|
key_group = key_parser.add_argument_group('key arguments')
|
|
key_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
key_parsers = key_parser.add_subparsers(
|
|
title='key sub-actions',
|
|
)
|
|
|
|
key_export_parser = key_parsers.add_parser(
|
|
'export',
|
|
help='Export a copy of the repository key for safekeeping in case the original goes missing or gets damaged',
|
|
description='Export a copy of the repository key for safekeeping in case the original goes missing or gets damaged',
|
|
add_help=False,
|
|
)
|
|
key_export_group = key_export_parser.add_argument_group('key export arguments')
|
|
key_export_group.add_argument(
|
|
'--paper',
|
|
action='store_true',
|
|
help='Export the key in a text format suitable for printing and later manual entry',
|
|
)
|
|
key_export_group.add_argument(
|
|
'--qr-html',
|
|
action='store_true',
|
|
help='Export the key in an HTML format suitable for printing and later manual entry or QR code scanning',
|
|
)
|
|
key_export_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to export the key for, defaults to the configured repository if there is only one',
|
|
)
|
|
key_export_group.add_argument(
|
|
'--path',
|
|
metavar='PATH',
|
|
help='Path to export the key to, defaults to stdout (but be careful about dirtying the output with --verbosity)',
|
|
)
|
|
key_export_group.add_argument(
|
|
'-h', '--help', action='help', help='Show this help message and exit'
|
|
)
|
|
|
|
borg_parser = action_parsers.add_parser(
|
|
'borg',
|
|
aliases=ACTION_ALIASES['borg'],
|
|
help='Run an arbitrary Borg command',
|
|
description="Run an arbitrary Borg command based on borgmatic's configuration",
|
|
add_help=False,
|
|
)
|
|
borg_group = borg_parser.add_argument_group('borg arguments')
|
|
borg_group.add_argument(
|
|
'--repository',
|
|
help='Path of repository to pass to Borg, defaults to the configured repositories',
|
|
)
|
|
borg_group.add_argument('--archive', help='Name of archive to pass to Borg (or "latest")')
|
|
borg_group.add_argument(
|
|
'--',
|
|
metavar='OPTION',
|
|
dest='options',
|
|
nargs='+',
|
|
help='Options to pass to Borg, command first ("create", "list", etc). "--" is optional. To specify the repository or the archive, you must use --repository or --archive instead of providing them here.',
|
|
)
|
|
borg_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
|
|
return global_parser, action_parsers, global_plus_action_parser
|
|
|
|
|
|
def parse_arguments(*unparsed_arguments):
|
|
'''
|
|
Given command-line arguments with which this script was invoked, parse the arguments and return
|
|
them as a dict mapping from action name (or "global") to an argparse.Namespace instance.
|
|
|
|
Raise ValueError if the arguments cannot be parsed.
|
|
Raise SystemExit with an error code of 0 if "--help" was requested.
|
|
'''
|
|
global_parser, action_parsers, global_plus_action_parser = make_parsers()
|
|
arguments, remaining_action_arguments = parse_arguments_for_actions(
|
|
unparsed_arguments, action_parsers.choices, global_parser
|
|
)
|
|
|
|
if not arguments['global'].config_paths:
|
|
arguments['global'].config_paths = collect.get_default_config_paths(expand_home=True)
|
|
|
|
for action_name in ('bootstrap', 'generate', 'validate'):
|
|
if (
|
|
action_name in arguments.keys() and len(arguments.keys()) > 2
|
|
): # 2 = 1 for 'global' + 1 for the action
|
|
raise ValueError(
|
|
f'The {action_name} action cannot be combined with other actions. Please run it separately.'
|
|
)
|
|
|
|
unknown_arguments = get_unparsable_arguments(remaining_action_arguments)
|
|
|
|
if unknown_arguments:
|
|
if '--help' in unknown_arguments or '-h' in unknown_arguments:
|
|
global_plus_action_parser.print_help()
|
|
sys.exit(0)
|
|
|
|
global_plus_action_parser.print_usage()
|
|
raise ValueError(
|
|
f"Unrecognized argument{'s' if len(unknown_arguments) > 1 else ''}: {' '.join(unknown_arguments)}"
|
|
)
|
|
|
|
if 'create' in arguments and arguments['create'].list_files and arguments['create'].progress:
|
|
raise ValueError(
|
|
'With the create action, only one of --list (--files) and --progress flags can be used.'
|
|
)
|
|
if 'create' in arguments and arguments['create'].list_files and arguments['create'].json:
|
|
raise ValueError(
|
|
'With the create action, only one of --list (--files) and --json flags can be used.'
|
|
)
|
|
|
|
if (
|
|
('list' in arguments and 'rinfo' in arguments and arguments['list'].json)
|
|
or ('list' in arguments and 'info' in arguments and arguments['list'].json)
|
|
or ('rinfo' in arguments and 'info' in arguments and arguments['rinfo'].json)
|
|
):
|
|
raise ValueError('With the --json flag, multiple actions cannot be used together.')
|
|
|
|
if (
|
|
'transfer' in arguments
|
|
and arguments['transfer'].archive
|
|
and arguments['transfer'].match_archives
|
|
):
|
|
raise ValueError(
|
|
'With the transfer action, only one of --archive and --match-archives flags can be used.'
|
|
)
|
|
|
|
if 'list' in arguments and (arguments['list'].prefix and arguments['list'].match_archives):
|
|
raise ValueError(
|
|
'With the list action, only one of --prefix or --match-archives flags can be used.'
|
|
)
|
|
|
|
if 'rlist' in arguments and (arguments['rlist'].prefix and arguments['rlist'].match_archives):
|
|
raise ValueError(
|
|
'With the rlist action, only one of --prefix or --match-archives flags can be used.'
|
|
)
|
|
|
|
if 'info' in arguments and (
|
|
(arguments['info'].archive and arguments['info'].prefix)
|
|
or (arguments['info'].archive and arguments['info'].match_archives)
|
|
or (arguments['info'].prefix and arguments['info'].match_archives)
|
|
):
|
|
raise ValueError(
|
|
'With the info action, only one of --archive, --prefix, or --match-archives flags can be used.'
|
|
)
|
|
|
|
return arguments
|