|
@@ -5,7 +5,6 @@ import traceback
|
|
|
|
|
|
try:
|
|
try:
|
|
import argparse
|
|
import argparse
|
|
- import configparser
|
|
|
|
import faulthandler
|
|
import faulthandler
|
|
import functools
|
|
import functools
|
|
import inspect
|
|
import inspect
|
|
@@ -20,7 +19,6 @@ try:
|
|
import subprocess
|
|
import subprocess
|
|
import textwrap
|
|
import textwrap
|
|
import time
|
|
import time
|
|
- from binascii import unhexlify
|
|
|
|
from datetime import datetime, timedelta
|
|
from datetime import datetime, timedelta
|
|
from io import TextIOWrapper
|
|
from io import TextIOWrapper
|
|
|
|
|
|
@@ -34,7 +32,7 @@ try:
|
|
from ..archive import Archive, ArchiveChecker, ArchiveRecreater, Statistics, is_special
|
|
from ..archive import Archive, ArchiveChecker, ArchiveRecreater, Statistics, is_special
|
|
from ..archive import BackupError, BackupOSError, backup_io, OsOpen, stat_update_check
|
|
from ..archive import BackupError, BackupOSError, backup_io, OsOpen, stat_update_check
|
|
from ..archive import FilesystemObjectProcessors, MetadataCollector, ChunksProcessor
|
|
from ..archive import FilesystemObjectProcessors, MetadataCollector, ChunksProcessor
|
|
- from ..cache import Cache, assert_secure, SecurityManager
|
|
|
|
|
|
+ from ..cache import Cache, SecurityManager
|
|
from ..constants import * # NOQA
|
|
from ..constants import * # NOQA
|
|
from ..compress import CompressionSpec
|
|
from ..compress import CompressionSpec
|
|
from ..crypto.key import key_creator, key_argument_names, tam_required_file
|
|
from ..crypto.key import key_creator, key_argument_names, tam_required_file
|
|
@@ -102,6 +100,7 @@ def get_func(args):
|
|
|
|
|
|
|
|
|
|
from .benchmarks import BenchmarkMixIn
|
|
from .benchmarks import BenchmarkMixIn
|
|
|
|
+from .config import ConfigMixIn
|
|
from .debug import DebugMixIn
|
|
from .debug import DebugMixIn
|
|
from .help import HelpMixIn
|
|
from .help import HelpMixIn
|
|
from .keys import KeysMixIn
|
|
from .keys import KeysMixIn
|
|
@@ -110,7 +109,7 @@ from .tar import TarMixIn
|
|
from .transfer import TransferMixIn
|
|
from .transfer import TransferMixIn
|
|
|
|
|
|
|
|
|
|
-class Archiver(DebugMixIn, TarMixIn, BenchmarkMixIn, KeysMixIn, LocksMixIn, HelpMixIn, TransferMixIn):
|
|
|
|
|
|
+class Archiver(ConfigMixIn, DebugMixIn, TarMixIn, BenchmarkMixIn, KeysMixIn, LocksMixIn, HelpMixIn, TransferMixIn):
|
|
def __init__(self, lock_wait=None, prog=None):
|
|
def __init__(self, lock_wait=None, prog=None):
|
|
self.exit_code = EXIT_SUCCESS
|
|
self.exit_code = EXIT_SUCCESS
|
|
self.lock_wait = lock_wait
|
|
self.lock_wait = lock_wait
|
|
@@ -1308,137 +1307,6 @@ class Archiver(DebugMixIn, TarMixIn, BenchmarkMixIn, KeysMixIn, LocksMixIn, Help
|
|
repository.commit(compact=True, threshold=threshold)
|
|
repository.commit(compact=True, threshold=threshold)
|
|
return EXIT_SUCCESS
|
|
return EXIT_SUCCESS
|
|
|
|
|
|
- @with_repository(exclusive=True, manifest=False)
|
|
|
|
- def do_config(self, args, repository):
|
|
|
|
- """get, set, and delete values in a repository or cache config file"""
|
|
|
|
-
|
|
|
|
- def repo_validate(section, name, value=None, check_value=True):
|
|
|
|
- if section not in ["repository"]:
|
|
|
|
- raise ValueError("Invalid section")
|
|
|
|
- if name in ["segments_per_dir", "last_segment_checked"]:
|
|
|
|
- if check_value:
|
|
|
|
- try:
|
|
|
|
- int(value)
|
|
|
|
- except ValueError:
|
|
|
|
- raise ValueError("Invalid value") from None
|
|
|
|
- elif name in ["max_segment_size", "additional_free_space", "storage_quota"]:
|
|
|
|
- if check_value:
|
|
|
|
- try:
|
|
|
|
- parse_file_size(value)
|
|
|
|
- except ValueError:
|
|
|
|
- raise ValueError("Invalid value") from None
|
|
|
|
- if name == "storage_quota":
|
|
|
|
- if parse_file_size(value) < parse_file_size("10M"):
|
|
|
|
- raise ValueError("Invalid value: storage_quota < 10M")
|
|
|
|
- elif name == "max_segment_size":
|
|
|
|
- if parse_file_size(value) >= MAX_SEGMENT_SIZE_LIMIT:
|
|
|
|
- raise ValueError("Invalid value: max_segment_size >= %d" % MAX_SEGMENT_SIZE_LIMIT)
|
|
|
|
- elif name in ["append_only"]:
|
|
|
|
- if check_value and value not in ["0", "1"]:
|
|
|
|
- raise ValueError("Invalid value")
|
|
|
|
- elif name in ["id"]:
|
|
|
|
- if check_value:
|
|
|
|
- try:
|
|
|
|
- bin_id = unhexlify(value)
|
|
|
|
- except:
|
|
|
|
- raise ValueError("Invalid value, must be 64 hex digits") from None
|
|
|
|
- if len(bin_id) != 32:
|
|
|
|
- raise ValueError("Invalid value, must be 64 hex digits")
|
|
|
|
- else:
|
|
|
|
- raise ValueError("Invalid name")
|
|
|
|
-
|
|
|
|
- def cache_validate(section, name, value=None, check_value=True):
|
|
|
|
- if section not in ["cache"]:
|
|
|
|
- raise ValueError("Invalid section")
|
|
|
|
- if name in ["previous_location"]:
|
|
|
|
- if check_value:
|
|
|
|
- Location(value)
|
|
|
|
- else:
|
|
|
|
- raise ValueError("Invalid name")
|
|
|
|
-
|
|
|
|
- def list_config(config):
|
|
|
|
- default_values = {
|
|
|
|
- "version": "1",
|
|
|
|
- "segments_per_dir": str(DEFAULT_SEGMENTS_PER_DIR),
|
|
|
|
- "max_segment_size": str(MAX_SEGMENT_SIZE_LIMIT),
|
|
|
|
- "additional_free_space": "0",
|
|
|
|
- "storage_quota": repository.storage_quota,
|
|
|
|
- "append_only": repository.append_only,
|
|
|
|
- }
|
|
|
|
- print("[repository]")
|
|
|
|
- for key in [
|
|
|
|
- "version",
|
|
|
|
- "segments_per_dir",
|
|
|
|
- "max_segment_size",
|
|
|
|
- "storage_quota",
|
|
|
|
- "additional_free_space",
|
|
|
|
- "append_only",
|
|
|
|
- "id",
|
|
|
|
- ]:
|
|
|
|
- value = config.get("repository", key, fallback=False)
|
|
|
|
- if value is None:
|
|
|
|
- value = default_values.get(key)
|
|
|
|
- if value is None:
|
|
|
|
- raise Error("The repository config is missing the %s key which has no default value" % key)
|
|
|
|
- print(f"{key} = {value}")
|
|
|
|
- for key in ["last_segment_checked"]:
|
|
|
|
- value = config.get("repository", key, fallback=None)
|
|
|
|
- if value is None:
|
|
|
|
- continue
|
|
|
|
- print(f"{key} = {value}")
|
|
|
|
-
|
|
|
|
- if not args.list:
|
|
|
|
- if args.name is None:
|
|
|
|
- self.print_error("No config key name was provided.")
|
|
|
|
- return self.exit_code
|
|
|
|
-
|
|
|
|
- try:
|
|
|
|
- section, name = args.name.split(".")
|
|
|
|
- except ValueError:
|
|
|
|
- section = args.cache and "cache" or "repository"
|
|
|
|
- name = args.name
|
|
|
|
-
|
|
|
|
- if args.cache:
|
|
|
|
- manifest, key = Manifest.load(repository, (Manifest.Operation.WRITE,))
|
|
|
|
- assert_secure(repository, manifest, self.lock_wait)
|
|
|
|
- cache = Cache(repository, key, manifest, lock_wait=self.lock_wait)
|
|
|
|
-
|
|
|
|
- try:
|
|
|
|
- if args.cache:
|
|
|
|
- cache.cache_config.load()
|
|
|
|
- config = cache.cache_config._config
|
|
|
|
- save = cache.cache_config.save
|
|
|
|
- validate = cache_validate
|
|
|
|
- else:
|
|
|
|
- config = repository.config
|
|
|
|
- save = lambda: repository.save_config(repository.path, repository.config) # noqa
|
|
|
|
- validate = repo_validate
|
|
|
|
-
|
|
|
|
- if args.delete:
|
|
|
|
- validate(section, name, check_value=False)
|
|
|
|
- config.remove_option(section, name)
|
|
|
|
- if len(config.options(section)) == 0:
|
|
|
|
- config.remove_section(section)
|
|
|
|
- save()
|
|
|
|
- elif args.list:
|
|
|
|
- list_config(config)
|
|
|
|
- elif args.value:
|
|
|
|
- validate(section, name, args.value)
|
|
|
|
- if section not in config.sections():
|
|
|
|
- config.add_section(section)
|
|
|
|
- config.set(section, name, args.value)
|
|
|
|
- save()
|
|
|
|
- else:
|
|
|
|
- try:
|
|
|
|
- print(config.get(section, name))
|
|
|
|
- except (configparser.NoOptionError, configparser.NoSectionError) as e:
|
|
|
|
- print(e, file=sys.stderr)
|
|
|
|
- return EXIT_WARNING
|
|
|
|
- return EXIT_SUCCESS
|
|
|
|
- finally:
|
|
|
|
- if args.cache:
|
|
|
|
- cache.close()
|
|
|
|
-
|
|
|
|
def preprocess_args(self, args):
|
|
def preprocess_args(self, args):
|
|
deprecations = [
|
|
deprecations = [
|
|
# ('--old', '--new' or None, 'Warning: "--old" has been deprecated. Use "--new" instead.'),
|
|
# ('--old', '--new' or None, 'Warning: "--old" has been deprecated. Use "--new" instead.'),
|
|
@@ -2003,47 +1871,6 @@ class Archiver(DebugMixIn, TarMixIn, BenchmarkMixIn, KeysMixIn, LocksMixIn, Help
|
|
help="set minimum threshold for saved space in PERCENT (Default: 10)",
|
|
help="set minimum threshold for saved space in PERCENT (Default: 10)",
|
|
)
|
|
)
|
|
|
|
|
|
- # borg config
|
|
|
|
- config_epilog = process_epilog(
|
|
|
|
- """
|
|
|
|
- This command gets and sets options in a local repository or cache config file.
|
|
|
|
- For security reasons, this command only works on local repositories.
|
|
|
|
-
|
|
|
|
- To delete a config value entirely, use ``--delete``. To list the values
|
|
|
|
- of the configuration file or the default values, use ``--list``. To get and existing
|
|
|
|
- key, pass only the key name. To set a key, pass both the key name and
|
|
|
|
- the new value. Keys can be specified in the format "section.name" or
|
|
|
|
- simply "name"; the section will default to "repository" and "cache" for
|
|
|
|
- the repo and cache configs, respectively.
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- By default, borg config manipulates the repository config file. Using ``--cache``
|
|
|
|
- edits the repository cache's config file instead.
|
|
|
|
- """
|
|
|
|
- )
|
|
|
|
- subparser = subparsers.add_parser(
|
|
|
|
- "config",
|
|
|
|
- parents=[common_parser],
|
|
|
|
- add_help=False,
|
|
|
|
- description=self.do_config.__doc__,
|
|
|
|
- epilog=config_epilog,
|
|
|
|
- formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
|
|
- help="get and set configuration values",
|
|
|
|
- )
|
|
|
|
- subparser.set_defaults(func=self.do_config)
|
|
|
|
- subparser.add_argument(
|
|
|
|
- "-c", "--cache", dest="cache", action="store_true", help="get and set values from the repo cache"
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- group = subparser.add_mutually_exclusive_group()
|
|
|
|
- group.add_argument(
|
|
|
|
- "-d", "--delete", dest="delete", action="store_true", help="delete the key from the config file"
|
|
|
|
- )
|
|
|
|
- group.add_argument("-l", "--list", dest="list", action="store_true", help="list the configuration of the repo")
|
|
|
|
-
|
|
|
|
- subparser.add_argument("name", metavar="NAME", nargs="?", help="name of config key")
|
|
|
|
- subparser.add_argument("value", metavar="VALUE", nargs="?", help="new value for key")
|
|
|
|
-
|
|
|
|
# borg create
|
|
# borg create
|
|
create_epilog = process_epilog(
|
|
create_epilog = process_epilog(
|
|
"""
|
|
"""
|
|
@@ -2425,6 +2252,7 @@ class Archiver(DebugMixIn, TarMixIn, BenchmarkMixIn, KeysMixIn, LocksMixIn, Help
|
|
subparser.add_argument("name", metavar="NAME", type=NameSpec, help="specify the archive name")
|
|
subparser.add_argument("name", metavar="NAME", type=NameSpec, help="specify the archive name")
|
|
subparser.add_argument("paths", metavar="PATH", nargs="*", type=str, help="paths to archive")
|
|
subparser.add_argument("paths", metavar="PATH", nargs="*", type=str, help="paths to archive")
|
|
|
|
|
|
|
|
+ self.build_parser_config(subparsers, common_parser, mid_common_parser)
|
|
self.build_parser_debug(subparsers, common_parser, mid_common_parser)
|
|
self.build_parser_debug(subparsers, common_parser, mid_common_parser)
|
|
self.build_parser_help(subparsers, common_parser, mid_common_parser, parser)
|
|
self.build_parser_help(subparsers, common_parser, mid_common_parser, parser)
|
|
|
|
|