diff --git a/src/borg/archiver/__init__.py b/src/borg/archiver/__init__.py index 0e510995c..9e3748334 100644 --- a/src/borg/archiver/__init__.py +++ b/src/borg/archiver/__init__.py @@ -40,7 +40,7 @@ try: from ..helpers import location_validator, archivename_validator, ChunkerParams, Location from ..helpers import NameSpec, CommentSpec, FilesCacheMode from ..helpers import BaseFormatter, ItemFormatter, ArchiveFormatter - from ..helpers import format_timedelta, format_file_size, parse_file_size, format_archive + from ..helpers import format_timedelta, format_file_size, format_archive, parse_storage_quota from ..helpers import remove_surrogates, bin_to_hex, eval_escapes from ..helpers import timestamp from ..helpers import get_cache_dir, os_stat @@ -60,7 +60,7 @@ try: from ..patterns import PatternMatcher from ..platform import get_flags from ..platform import uid2user, gid2group - from ..remote import RepositoryServer, RemoteRepository + from ..remote import RemoteRepository from ..selftest import selftest except BaseException: # an unhandled exception in the try-block would cause the borg cli command to exit with rc 1 due to python's @@ -77,13 +77,6 @@ STATS_HEADER = " Original size Deduplicated size" PURE_PYTHON_MSGPACK_WARNING = "Using a pure-python msgpack! This will result in lower performance." -def parse_storage_quota(storage_quota): - parsed = parse_file_size(storage_quota) - if parsed < parse_file_size("10M"): - raise argparse.ArgumentTypeError("quota is too small (%s). At least 10M are required." % storage_quota) - return parsed - - def get_func(args): # This works around https://bugs.python.org/issue9351 # func is used at the leaf parsers of the argparse parser tree, @@ -107,6 +100,7 @@ from .keys import KeysMixIn from .locks import LocksMixIn from .mount import MountMixIn from .prune import PruneMixIn +from .serve import ServeMixIn from .tar import TarMixIn from .transfer import TransferMixIn @@ -124,6 +118,7 @@ class Archiver( MountMixIn, PruneMixIn, HelpMixIn, + ServeMixIn, TransferMixIn, ): def __init__(self, lock_wait=None, prog=None): @@ -159,16 +154,6 @@ class Archiver( matcher.add_includepaths(include_paths) return matcher - def do_serve(self, args): - """Start in server mode. This command is usually not used manually.""" - RepositoryServer( - restrict_to_paths=args.restrict_to_paths, - restrict_to_repositories=args.restrict_to_repositories, - append_only=args.append_only, - storage_quota=args.storage_quota, - ).serve() - return EXIT_SUCCESS - @with_repository(create=True, exclusive=True, manifest=False) @with_other_repository(key=True, compatibility=(Manifest.Operation.READ,)) def do_rcreate(self, args, repository, *, other_repository=None, other_key=None): @@ -2546,64 +2531,7 @@ class Archiver( "newname", metavar="NEWNAME", type=archivename_validator(), help="specify the new archive name" ) - # borg serve - serve_epilog = process_epilog( - """ - This command starts a repository server process. This command is usually not used manually. - """ - ) - subparser = subparsers.add_parser( - "serve", - parents=[common_parser], - add_help=False, - description=self.do_serve.__doc__, - epilog=serve_epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, - help="start repository server process", - ) - subparser.set_defaults(func=self.do_serve) - subparser.add_argument( - "--restrict-to-path", - metavar="PATH", - dest="restrict_to_paths", - action="append", - help="restrict repository access to PATH. " - "Can be specified multiple times to allow the client access to several directories. " - "Access to all sub-directories is granted implicitly; PATH doesn't need to directly point to a repository.", - ) - subparser.add_argument( - "--restrict-to-repository", - metavar="PATH", - dest="restrict_to_repositories", - action="append", - help="restrict repository access. Only the repository located at PATH " - "(no sub-directories are considered) is accessible. " - "Can be specified multiple times to allow the client access to several repositories. " - "Unlike ``--restrict-to-path`` sub-directories are not accessible; " - "PATH needs to directly point at a repository location. " - "PATH may be an empty directory or the last element of PATH may not exist, in which case " - "the client may initialize a repository there.", - ) - subparser.add_argument( - "--append-only", - dest="append_only", - action="store_true", - help="only allow appending to repository segment files. Note that this only " - "affects the low level structure of the repository, and running `delete` " - "or `prune` will still be allowed. See :ref:`append_only_mode` in Additional " - "Notes for more details.", - ) - subparser.add_argument( - "--storage-quota", - metavar="QUOTA", - dest="storage_quota", - type=parse_storage_quota, - default=None, - help="Override storage quota of the repository (e.g. 5G, 1.5T). " - "When a new repository is initialized, sets the storage quota on the new " - "repository as well. Default: no quota.", - ) - + self.build_parser_serve(subparsers, common_parser, mid_common_parser) self.build_parser_tar(subparsers, common_parser, mid_common_parser) self.build_parser_transfer(subparsers, common_parser, mid_common_parser) diff --git a/src/borg/archiver/serve.py b/src/borg/archiver/serve.py new file mode 100644 index 000000000..4aa6c4a57 --- /dev/null +++ b/src/borg/archiver/serve.py @@ -0,0 +1,82 @@ +import argparse + +from ..constants import * # NOQA +from ..helpers import EXIT_SUCCESS +from ..helpers import parse_storage_quota +from ..remote import RepositoryServer + +from ..logger import create_logger + +logger = create_logger() + + +class ServeMixIn: + def do_serve(self, args): + """Start in server mode. This command is usually not used manually.""" + RepositoryServer( + restrict_to_paths=args.restrict_to_paths, + restrict_to_repositories=args.restrict_to_repositories, + append_only=args.append_only, + storage_quota=args.storage_quota, + ).serve() + return EXIT_SUCCESS + + def build_parser_serve(self, subparsers, common_parser, mid_common_parser): + from .common import process_epilog + + serve_epilog = process_epilog( + """ + This command starts a repository server process. This command is usually not used manually. + """ + ) + subparser = subparsers.add_parser( + "serve", + parents=[common_parser], + add_help=False, + description=self.do_serve.__doc__, + epilog=serve_epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, + help="start repository server process", + ) + subparser.set_defaults(func=self.do_serve) + subparser.add_argument( + "--restrict-to-path", + metavar="PATH", + dest="restrict_to_paths", + action="append", + help="restrict repository access to PATH. " + "Can be specified multiple times to allow the client access to several directories. " + "Access to all sub-directories is granted implicitly; PATH doesn't need to directly point to a repository.", + ) + subparser.add_argument( + "--restrict-to-repository", + metavar="PATH", + dest="restrict_to_repositories", + action="append", + help="restrict repository access. Only the repository located at PATH " + "(no sub-directories are considered) is accessible. " + "Can be specified multiple times to allow the client access to several repositories. " + "Unlike ``--restrict-to-path`` sub-directories are not accessible; " + "PATH needs to directly point at a repository location. " + "PATH may be an empty directory or the last element of PATH may not exist, in which case " + "the client may initialize a repository there.", + ) + subparser.add_argument( + "--append-only", + dest="append_only", + action="store_true", + help="only allow appending to repository segment files. Note that this only " + "affects the low level structure of the repository, and running `delete` " + "or `prune` will still be allowed. See :ref:`append_only_mode` in Additional " + "Notes for more details.", + ) + subparser.add_argument( + "--storage-quota", + metavar="QUOTA", + dest="storage_quota", + type=parse_storage_quota, + default=None, + help="Override storage quota of the repository (e.g. 5G, 1.5T). " + "When a new repository is initialized, sets the storage quota on the new " + "repository as well. Default: no quota.", + ) diff --git a/src/borg/helpers/parseformat.py b/src/borg/helpers/parseformat.py index b058ada99..69ea17237 100644 --- a/src/borg/helpers/parseformat.py +++ b/src/borg/helpers/parseformat.py @@ -257,6 +257,13 @@ def parse_file_size(s): return int(float(s) * factor) +def parse_storage_quota(storage_quota): + parsed = parse_file_size(storage_quota) + if parsed < parse_file_size("10M"): + raise argparse.ArgumentTypeError("quota is too small (%s). At least 10M are required." % storage_quota) + return parsed + + def sizeof_fmt(num, suffix="B", units=None, power=None, sep="", precision=2, sign=False): sign = "+" if sign and num > 0 else "" fmt = "{0:{1}.{2}f}{3}{4}{5}"