mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-11 01:41:57 -04:00
Merge pull request #8797 from ThomasWaldmann/remove-quota
remove remainders of quota support
This commit is contained in:
commit
94df5eee21
19 changed files with 21 additions and 192 deletions
|
|
@ -6,7 +6,6 @@ Hosting repositories
|
|||
====================
|
||||
|
||||
This sections shows how to provide repository storage securely for users.
|
||||
Optionally, each user can have a storage quota.
|
||||
|
||||
Repositories are accessed through SSH. Each user of the service should
|
||||
have her own login which is only able to access the user's files.
|
||||
|
|
@ -56,18 +55,6 @@ multiple times to permit access to more than one repository.
|
|||
The repository may not exist yet; it can be initialized by the user,
|
||||
which allows for encryption.
|
||||
|
||||
**Storage quotas** can be enabled by adding the ``--storage-quota`` option
|
||||
to the ``borg serve`` command line::
|
||||
|
||||
restrict,command="borg serve --storage-quota 20G ..." ...
|
||||
|
||||
The storage quotas of repositories are completely independent. If a
|
||||
client is able to access multiple repositories, each repository
|
||||
can be filled to the specified quota.
|
||||
|
||||
If storage quotas are used, ensure that all deployed Borg releases
|
||||
support storage quotas.
|
||||
|
||||
**Specificities: Append-only repositories**
|
||||
|
||||
Running ``borg init`` via a ``borg serve --append-only`` server will **not**
|
||||
|
|
|
|||
|
|
@ -610,8 +610,6 @@ Errors
|
|||
The parent path of the repo directory [{}] does not exist.
|
||||
Repository.PathAlreadyExists rc: 19 traceback: no
|
||||
There is already something at {}.
|
||||
Repository.StorageQuotaExceeded rc: 20 traceback: no
|
||||
The storage quota ({}) has been exceeded ({}). Try deleting some archives.
|
||||
Repository.PathPermissionDenied rc: 21 traceback: no
|
||||
Permission denied to {}.
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ Also helpful:
|
|||
does not have free space any more.
|
||||
- if you use LVM: use a LV + a filesystem that you can resize later and have
|
||||
some unallocated PEs you can add to the LV.
|
||||
- consider using quotas
|
||||
- consider using quotas (e.g. fs quota, quota settings of storage provider)
|
||||
- use `prune` and `compact` regularly
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ complete -c borg -l 'rsh' -d 'Use COMMAND instead of s
|
|||
set -l encryption_modes "none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2"
|
||||
complete -c borg -f -s e -l 'encryption' -d 'Encryption key MODE' -a "$encryption_modes" -n "__fish_seen_subcommand_from init"
|
||||
complete -c borg -f -l 'append-only' -d 'Create an append-only mode repository' -n "__fish_seen_subcommand_from init"
|
||||
complete -c borg -f -l 'storage-quota' -d 'Set storage QUOTA of the repository' -n "__fish_seen_subcommand_from init"
|
||||
complete -c borg -f -l 'make-parent-dirs' -d 'Create parent directories' -n "__fish_seen_subcommand_from init"
|
||||
|
||||
# borg create options
|
||||
|
|
@ -316,7 +315,6 @@ complete -c borg -f -l 'strip-components' -d 'Remove NUMBER of leading
|
|||
complete -c borg -l 'restrict-to-path' -d 'Restrict repository access to PATH' -n "__fish_seen_subcommand_from serve"
|
||||
complete -c borg -l 'restrict-to-repository' -d 'Restrict repository access at PATH' -n "__fish_seen_subcommand_from serve"
|
||||
complete -c borg -f -l 'append-only' -d 'Only allow appending to repository' -n "__fish_seen_subcommand_from serve"
|
||||
complete -c borg -f -l 'storage-quota' -d 'Override storage QUOTA of the repository' -n "__fish_seen_subcommand_from serve"
|
||||
|
||||
# borg config
|
||||
complete -c borg -f -s c -l 'cache' -d 'Get/set/list values in the repo cache' -n "__fish_seen_subcommand_from config"
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ class Archiver(
|
|||
# client is allowed to specify the allowlisted options,
|
||||
# everything else comes from the forced "borg serve" command (or the defaults).
|
||||
# stuff from denylist must never be used from the client.
|
||||
denylist = {"restrict_to_paths", "restrict_to_repositories", "append_only", "storage_quota", "umask"}
|
||||
denylist = {"restrict_to_paths", "restrict_to_repositories", "append_only", "umask"}
|
||||
allowlist = {"debug_topics", "lock_wait", "log_level"}
|
||||
not_present = object()
|
||||
for attr_name in allowlist:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from ..logger import create_logger
|
|||
logger = create_logger(__name__)
|
||||
|
||||
|
||||
def get_repository(location, *, create, exclusive, lock_wait, lock, append_only, storage_quota, args, v1_or_v2):
|
||||
def get_repository(location, *, create, exclusive, lock_wait, lock, append_only, args, v1_or_v2):
|
||||
if location.proto in ("ssh", "socket"):
|
||||
RemoteRepoCls = LegacyRemoteRepository if v1_or_v2 else RemoteRepository
|
||||
repository = RemoteRepoCls(
|
||||
|
|
@ -45,25 +45,13 @@ def get_repository(location, *, create, exclusive, lock_wait, lock, append_only,
|
|||
|
||||
elif location.proto in ("sftp", "file", "rclone") and not v1_or_v2: # stuff directly supported by borgstore
|
||||
repository = Repository(
|
||||
location,
|
||||
create=create,
|
||||
exclusive=exclusive,
|
||||
lock_wait=lock_wait,
|
||||
lock=lock,
|
||||
append_only=append_only,
|
||||
storage_quota=storage_quota,
|
||||
location, create=create, exclusive=exclusive, lock_wait=lock_wait, lock=lock, append_only=append_only
|
||||
)
|
||||
|
||||
else:
|
||||
RepoCls = LegacyRepository if v1_or_v2 else Repository
|
||||
repository = RepoCls(
|
||||
location.path,
|
||||
create=create,
|
||||
exclusive=exclusive,
|
||||
lock_wait=lock_wait,
|
||||
lock=lock,
|
||||
append_only=append_only,
|
||||
storage_quota=storage_quota,
|
||||
location.path, create=create, exclusive=exclusive, lock_wait=lock_wait, lock=lock, append_only=append_only
|
||||
)
|
||||
return repository
|
||||
|
||||
|
|
@ -127,7 +115,6 @@ def with_repository(
|
|||
assert isinstance(exclusive, bool)
|
||||
lock = getattr(args, "lock", _lock)
|
||||
append_only = getattr(args, "append_only", False)
|
||||
storage_quota = getattr(args, "storage_quota", None)
|
||||
|
||||
repository = get_repository(
|
||||
location,
|
||||
|
|
@ -136,7 +123,6 @@ def with_repository(
|
|||
lock_wait=self.lock_wait,
|
||||
lock=lock,
|
||||
append_only=append_only,
|
||||
storage_quota=storage_quota,
|
||||
args=args,
|
||||
v1_or_v2=False,
|
||||
)
|
||||
|
|
@ -205,7 +191,6 @@ def with_other_repository(manifest=False, cache=False, compatibility=None):
|
|||
lock_wait=self.lock_wait,
|
||||
lock=True,
|
||||
append_only=False,
|
||||
storage_quota=None,
|
||||
args=args,
|
||||
v1_or_v2=v1_or_v2,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ from ..constants import * # NOQA
|
|||
from ..crypto.key import key_creator, key_argument_names
|
||||
from ..helpers import CancelledByUser, CommandError
|
||||
from ..helpers import location_validator, Location
|
||||
from ..helpers import parse_storage_quota
|
||||
from ..manifest import Manifest
|
||||
|
||||
from ..logger import create_logger
|
||||
|
|
@ -19,8 +18,6 @@ class RepoCreateMixIn:
|
|||
@with_other_repository(manifest=True, compatibility=(Manifest.Operation.READ,))
|
||||
def do_repo_create(self, args, repository, *, other_repository=None, other_manifest=None):
|
||||
"""Create a new, empty repository"""
|
||||
if args.storage_quota is not None:
|
||||
raise CommandError("storage-quota is not supported (yet?)")
|
||||
if args.append_only:
|
||||
raise CommandError("append-only is not supported (yet?)")
|
||||
other_key = other_manifest.key if other_manifest is not None else None
|
||||
|
|
@ -236,15 +233,6 @@ class RepoCreateMixIn:
|
|||
"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",
|
||||
default=None,
|
||||
type=parse_storage_quota,
|
||||
action=Highlander,
|
||||
help="Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota.",
|
||||
)
|
||||
subparser.add_argument(
|
||||
"--copy-crypt-key",
|
||||
dest="copy_crypt_key",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import textwrap
|
|||
|
||||
from ._common import with_repository
|
||||
from ..constants import * # NOQA
|
||||
from ..helpers import bin_to_hex, json_print, basic_json_data, format_file_size
|
||||
from ..helpers import bin_to_hex, json_print, basic_json_data
|
||||
from ..manifest import Manifest
|
||||
|
||||
from ..logger import create_logger
|
||||
|
|
@ -50,15 +50,6 @@ class RepoInfoMixIn:
|
|||
)
|
||||
)
|
||||
|
||||
response = repository.info()
|
||||
storage_quota = response["storage_quota"]
|
||||
used = format_file_size(response["storage_quota_use"], iec=args.iec)
|
||||
|
||||
output += f"\nStorage quota: {used} used"
|
||||
if storage_quota:
|
||||
output += f" out of {format_file_size(storage_quota, iec=args.iec)}"
|
||||
output += "\n"
|
||||
|
||||
if hasattr(info["cache"], "path"):
|
||||
output += "Cache: {cache.path}\n".format(**info)
|
||||
output += "Security dir: {security_dir}\n".format(**info)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import argparse
|
||||
|
||||
from ._common import Highlander
|
||||
from ..constants import * # NOQA
|
||||
from ..helpers import parse_storage_quota, CommandError
|
||||
from ..helpers import CommandError
|
||||
from ..remote import RepositoryServer
|
||||
|
||||
from ..logger import create_logger
|
||||
|
|
@ -15,13 +14,10 @@ class ServeMixIn:
|
|||
"""Start in server mode. This command is usually not used manually."""
|
||||
if args.append_only:
|
||||
raise CommandError("append-only is not supported (yet?)")
|
||||
if args.storage_quota is not None:
|
||||
raise CommandError("storage-quota is not supported (yet?)")
|
||||
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,
|
||||
use_socket=args.use_socket,
|
||||
).serve()
|
||||
|
||||
|
|
@ -84,14 +80,3 @@ class ServeMixIn:
|
|||
"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,
|
||||
action=Highlander,
|
||||
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.",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from .parseformat import bin_to_hex, hex_to_bin, safe_encode, safe_decode
|
|||
from .parseformat import text_to_json, binary_to_json, remove_surrogates, join_cmd
|
||||
from .parseformat import eval_escapes, decode_dict, positive_int_validator, interval
|
||||
from .parseformat import PathSpec, SortBySpec, ChunkerParams, FilesCacheMode, partial_format, DatetimeWrapper
|
||||
from .parseformat import format_file_size, parse_file_size, FileSize, parse_storage_quota
|
||||
from .parseformat import format_file_size, parse_file_size, FileSize
|
||||
from .parseformat import sizeof_fmt, sizeof_fmt_iec, sizeof_fmt_decimal, Location, text_validator
|
||||
from .parseformat import format_line, replace_placeholders, PlaceholderError, relative_time_marker_validator
|
||||
from .parseformat import format_archive, parse_stringified_list, clean_lines
|
||||
|
|
|
|||
|
|
@ -357,13 +357,6 @@ 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}"
|
||||
|
|
|
|||
|
|
@ -409,9 +409,6 @@ class LegacyRemoteRepository:
|
|||
topic = "borg.debug." + topic
|
||||
if "repository" in topic:
|
||||
opts.append("--debug-topic=%s" % topic)
|
||||
|
||||
if "storage_quota" in args and args.storage_quota:
|
||||
opts.append("--storage-quota=%s" % args.storage_quota)
|
||||
env_vars = []
|
||||
if testing:
|
||||
return env_vars + [sys.executable, "-m", "borg", "serve"] + opts + self.extra_test_args
|
||||
|
|
|
|||
|
|
@ -183,10 +183,7 @@ class LegacyRepository:
|
|||
|
||||
exit_mcode = 19
|
||||
|
||||
class StorageQuotaExceeded(Error):
|
||||
"""The storage quota ({}) has been exceeded ({}). Try deleting some archives."""
|
||||
|
||||
exit_mcode = 20
|
||||
# StorageQuotaExceeded was exit_mcode = 20
|
||||
|
||||
class PathPermissionDenied(Error):
|
||||
"""Permission denied to {}."""
|
||||
|
|
@ -194,15 +191,7 @@ class LegacyRepository:
|
|||
exit_mcode = 21
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path,
|
||||
create=False,
|
||||
exclusive=False,
|
||||
lock_wait=None,
|
||||
lock=True,
|
||||
append_only=False,
|
||||
storage_quota=None,
|
||||
send_log_cb=None,
|
||||
self, path, create=False, exclusive=False, lock_wait=None, lock=True, append_only=False, send_log_cb=None
|
||||
):
|
||||
self.path = os.path.abspath(path)
|
||||
self._location = Location("file://%s" % self.path)
|
||||
|
|
@ -230,8 +219,6 @@ class LegacyRepository:
|
|||
self.created = False
|
||||
self.exclusive = exclusive
|
||||
self.append_only = append_only
|
||||
self.storage_quota = storage_quota
|
||||
self.storage_quota_use = 0
|
||||
self.transaction_doomed = None
|
||||
# v2 is the default repo version for borg 2.0
|
||||
# v1 repos must only be used in a read-only way, e.g. for
|
||||
|
|
@ -290,13 +277,9 @@ class LegacyRepository:
|
|||
"""
|
||||
Raise an exception if a repository already exists at *path* or any parent directory.
|
||||
|
||||
Checking parent directories is done for two reasons:
|
||||
(1) It's just a weird thing to do, and usually not intended. A Borg using the "parent" repository
|
||||
may be confused, or we may accidentally put stuff into the "data/" or "data/<n>/" directories.
|
||||
(2) When implementing repository quotas (which we currently don't), it's important to prohibit
|
||||
folks from creating quota-free repositories. Since no one can create a repository within another
|
||||
repository, user's can only use the quota'd repository, when their --restrict-to-path points
|
||||
at the user's repository.
|
||||
Checking parent directories is done because it's just a weird thing to do, and usually not intended.
|
||||
A Borg using the "parent" repository may be confused, or we may accidentally put stuff into the "data/" or
|
||||
"data/<n>/" directories.
|
||||
"""
|
||||
try:
|
||||
st = os.stat(path)
|
||||
|
|
@ -345,10 +328,6 @@ class LegacyRepository:
|
|||
config.set("repository", "segments_per_dir", str(DEFAULT_SEGMENTS_PER_DIR))
|
||||
config.set("repository", "max_segment_size", str(DEFAULT_MAX_SEGMENT_SIZE))
|
||||
config.set("repository", "append_only", str(int(self.append_only)))
|
||||
if self.storage_quota:
|
||||
config.set("repository", "storage_quota", str(self.storage_quota))
|
||||
else:
|
||||
config.set("repository", "storage_quota", "0")
|
||||
config.set("repository", "additional_free_space", "0")
|
||||
config.set("repository", "id", bin_to_hex(os.urandom(32)))
|
||||
self.save_config(path, config)
|
||||
|
|
@ -492,9 +471,6 @@ class LegacyRepository:
|
|||
# append_only can be set in the constructor
|
||||
# it shouldn't be overridden (True -> False) here
|
||||
self.append_only = self.append_only or self.config.getboolean("repository", "append_only", fallback=False)
|
||||
if self.storage_quota is None:
|
||||
# self.storage_quota is None => no explicit storage_quota was specified, use repository setting.
|
||||
self.storage_quota = parse_file_size(self.config.get("repository", "storage_quota", fallback=0))
|
||||
self.id = hex_to_bin(self.config.get("repository", "id").strip(), length=32)
|
||||
self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
|
||||
|
||||
|
|
@ -504,15 +480,12 @@ class LegacyRepository:
|
|||
return
|
||||
hints = self._unpack_hints(transaction_id)
|
||||
self.version = hints["version"]
|
||||
self.storage_quota_use = hints["storage_quota_use"]
|
||||
self.shadow_index = hints["shadow_index"]
|
||||
|
||||
def info(self):
|
||||
"""return some infos about the repo (must be opened first)"""
|
||||
info = dict(id=self.id, version=self.version, append_only=self.append_only)
|
||||
self._load_hints()
|
||||
info["storage_quota"] = self.storage_quota
|
||||
info["storage_quota_use"] = self.storage_quota_use
|
||||
return info
|
||||
|
||||
def close(self):
|
||||
|
|
@ -604,7 +577,6 @@ class LegacyRepository:
|
|||
if transaction_id is None:
|
||||
self.segments = {} # XXX bad name: usage_count_of_segment_x = self.segments[x]
|
||||
self.compact = FreeSpace() # XXX bad name: freeable_space_of_segment_x = self.compact[x]
|
||||
self.storage_quota_use = 0
|
||||
self.shadow_index.clear()
|
||||
else:
|
||||
if do_cleanup:
|
||||
|
|
@ -626,7 +598,6 @@ class LegacyRepository:
|
|||
logger.debug("Upgrading from v1 hints.%d", transaction_id)
|
||||
self.segments = hints["segments"]
|
||||
self.compact = FreeSpace()
|
||||
self.storage_quota_use = 0
|
||||
self.shadow_index = {}
|
||||
for segment in sorted(hints["compact"]):
|
||||
logger.debug("Rebuilding sparse info for segment %d", segment)
|
||||
|
|
@ -637,7 +608,6 @@ class LegacyRepository:
|
|||
else:
|
||||
self.segments = hints["segments"]
|
||||
self.compact = FreeSpace(hints["compact"])
|
||||
self.storage_quota_use = hints.get("storage_quota_use", 0)
|
||||
self.shadow_index = hints.get("shadow_index", {})
|
||||
# Drop uncommitted segments in the shadow index
|
||||
for key, shadowed_segments in self.shadow_index.items():
|
||||
|
|
@ -653,13 +623,7 @@ class LegacyRepository:
|
|||
def rename_tmp(file):
|
||||
os.replace(file + ".tmp", file)
|
||||
|
||||
hints = {
|
||||
"version": 2,
|
||||
"segments": self.segments,
|
||||
"compact": self.compact,
|
||||
"storage_quota_use": self.storage_quota_use,
|
||||
"shadow_index": self.shadow_index,
|
||||
}
|
||||
hints = {"version": 2, "segments": self.segments, "compact": self.compact, "shadow_index": self.shadow_index}
|
||||
integrity = {
|
||||
# Integrity version started at 2, the current hints version.
|
||||
# Thus, integrity version == hints version, for now.
|
||||
|
|
@ -783,7 +747,6 @@ class LegacyRepository:
|
|||
if not self.compact:
|
||||
logger.debug("Nothing to do: compact empty")
|
||||
return
|
||||
quota_use_before = self.storage_quota_use
|
||||
index_transaction_id = self.get_index_transaction_id()
|
||||
segments = self.segments
|
||||
unused = [] # list of segments, that are not used anymore
|
||||
|
|
@ -855,9 +818,6 @@ class LegacyRepository:
|
|||
segments.setdefault(new_segment, 0)
|
||||
segments[new_segment] += 1
|
||||
segments[segment] -= 1
|
||||
if tag == TAG_PUT:
|
||||
# old tag is PUT, but new will be PUT2 and use a bit more storage
|
||||
self.storage_quota_use += self.io.ENTRY_HASH_SIZE
|
||||
elif tag in (TAG_PUT2, TAG_PUT) and not is_index_object:
|
||||
# If this is a PUT shadowed by a later tag, then it will be gone when this segment is deleted after
|
||||
# this loop. Therefore it is removed from the shadow index.
|
||||
|
|
@ -867,7 +827,6 @@ class LegacyRepository:
|
|||
# do not remove entry with empty shadowed_segments list here,
|
||||
# it is needed for shadowed_put_exists code (see below)!
|
||||
pass
|
||||
self.storage_quota_use -= header_size(tag) + len(data)
|
||||
elif tag == TAG_DELETE and not in_index:
|
||||
# If the shadow index doesn't contain this key, then we can't say if there's a shadowed older tag,
|
||||
# therefore we do not drop the delete, but write it to a current segment.
|
||||
|
|
@ -945,8 +904,6 @@ class LegacyRepository:
|
|||
self._send_log()
|
||||
complete_xfer(intermediate=False)
|
||||
self.io.clear_empty_dirs()
|
||||
quota_use_after = self.storage_quota_use
|
||||
logger.info("Compaction freed about %s repository space.", format_file_size(quota_use_before - quota_use_after))
|
||||
logger.debug("Compaction completed.")
|
||||
|
||||
def replay_segments(self, index_transaction_id, segments_transaction_id):
|
||||
|
|
@ -990,7 +947,6 @@ class LegacyRepository:
|
|||
pass
|
||||
self.index[key] = NSIndex1Entry(segment, offset)
|
||||
self.segments[segment] += 1
|
||||
self.storage_quota_use += header_size(tag) + size
|
||||
elif tag == TAG_DELETE:
|
||||
try:
|
||||
# if the deleted PUT is not in the index, there is nothing to clean up
|
||||
|
|
@ -1232,20 +1188,14 @@ class LegacyRepository:
|
|||
pass
|
||||
else:
|
||||
# this put call supersedes a previous put to same id.
|
||||
# it is essential to do a delete first to get correct quota bookkeeping
|
||||
# and also a correctly updated shadow_index, so that the compaction code
|
||||
# does not wrongly resurrect an old PUT by dropping a DEL that is still needed.
|
||||
# it is essential to do a delete first to get a correctly updated shadow_index,
|
||||
# so that the compaction code does not wrongly resurrect an old PUT by
|
||||
# dropping a DEL that is still needed.
|
||||
self._delete(id, in_index.segment, in_index.offset, 0)
|
||||
segment, offset = self.io.write_put(id, data)
|
||||
self.storage_quota_use += header_size(TAG_PUT2) + len(data)
|
||||
self.segments.setdefault(segment, 0)
|
||||
self.segments[segment] += 1
|
||||
self.index[id] = NSIndex1Entry(segment, offset)
|
||||
if self.storage_quota and self.storage_quota_use > self.storage_quota:
|
||||
self.transaction_doomed = self.StorageQuotaExceeded(
|
||||
format_file_size(self.storage_quota), format_file_size(self.storage_quota_use)
|
||||
)
|
||||
raise self.transaction_doomed
|
||||
|
||||
def delete(self, id, wait=True):
|
||||
"""delete a repo object
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ class RepositoryServer: # pragma: no cover
|
|||
"store_move",
|
||||
)
|
||||
|
||||
def __init__(self, restrict_to_paths, restrict_to_repositories, append_only, storage_quota, use_socket):
|
||||
def __init__(self, restrict_to_paths, restrict_to_repositories, append_only, use_socket):
|
||||
self.repository = None
|
||||
self.RepoCls = None
|
||||
self.rpc_methods = ("open", "close", "negotiate")
|
||||
|
|
@ -194,7 +194,6 @@ class RepositoryServer: # pragma: no cover
|
|||
# whatever the client wants, except when initializing a new repository
|
||||
# (see RepositoryServer.open below).
|
||||
self.append_only = append_only
|
||||
self.storage_quota = storage_quota
|
||||
self.client_version = None # we update this after client sends version information
|
||||
if use_socket is False:
|
||||
self.socket_path = None
|
||||
|
|
@ -276,7 +275,6 @@ class RepositoryServer: # pragma: no cover
|
|||
Repository.PathAlreadyExists,
|
||||
PathNotAllowed,
|
||||
Repository.InsufficientFreeSpaceError,
|
||||
Repository.StorageQuotaExceeded,
|
||||
)
|
||||
# logger.exception(e)
|
||||
ex_short = traceback.format_exception_only(e.__class__, e)
|
||||
|
|
@ -407,7 +405,6 @@ class RepositoryServer: # pragma: no cover
|
|||
lock_wait=lock_wait,
|
||||
lock=lock,
|
||||
append_only=append_only,
|
||||
storage_quota=self.storage_quota,
|
||||
exclusive=exclusive,
|
||||
send_log_cb=self.send_queued_log,
|
||||
)
|
||||
|
|
@ -735,9 +732,6 @@ class RemoteRepository:
|
|||
topic = "borg.debug." + topic
|
||||
if "repository" in topic:
|
||||
opts.append("--debug-topic=%s" % topic)
|
||||
|
||||
if "storage_quota" in args and args.storage_quota:
|
||||
opts.append("--storage-quota=%s" % args.storage_quota)
|
||||
env_vars = []
|
||||
if testing:
|
||||
return env_vars + [sys.executable, "-m", "borg", "serve"] + opts + self.extra_test_args
|
||||
|
|
@ -834,8 +828,6 @@ class RemoteRepository:
|
|||
raise Repository.InsufficientFreeSpaceError(args[0], args[1])
|
||||
elif error == "InvalidRepositoryConfig":
|
||||
raise Repository.InvalidRepositoryConfig(self.location.processed, args[1])
|
||||
elif error == "StorageQuotaExceeded":
|
||||
raise Repository.StorageQuotaExceeded(args[0], args[1])
|
||||
else:
|
||||
raise self.RPCError(unpacked)
|
||||
|
||||
|
|
|
|||
|
|
@ -86,10 +86,7 @@ class Repository:
|
|||
|
||||
exit_mcode = 19
|
||||
|
||||
class StorageQuotaExceeded(Error):
|
||||
"""The storage quota ({}) has been exceeded ({}). Try deleting some archives."""
|
||||
|
||||
exit_mcode = 20
|
||||
# StorageQuotaExceeded was exit_mcode = 20
|
||||
|
||||
class PathPermissionDenied(Error):
|
||||
"""Permission denied to {}."""
|
||||
|
|
@ -104,7 +101,6 @@ class Repository:
|
|||
lock_wait=1.0,
|
||||
lock=True,
|
||||
append_only=False,
|
||||
storage_quota=None,
|
||||
send_log_cb=None,
|
||||
):
|
||||
if isinstance(path_or_location, Location):
|
||||
|
|
@ -144,8 +140,6 @@ class Repository:
|
|||
self.acceptable_repo_versions = (3,)
|
||||
self.opened = False
|
||||
self.append_only = append_only # XXX not implemented / not implementable
|
||||
self.storage_quota = storage_quota # XXX not implemented
|
||||
self.storage_quota_use = 0 # XXX not implemented
|
||||
self.lock = None
|
||||
self.do_lock = lock
|
||||
self.lock_wait = lock_wait
|
||||
|
|
@ -260,13 +254,7 @@ class Repository:
|
|||
"""return some infos about the repo (must be opened first)"""
|
||||
# note: don't do anything expensive here or separate the lock refresh into a separate method.
|
||||
self._lock_refresh() # do not remove, see do_with_lock()
|
||||
info = dict(
|
||||
id=self.id,
|
||||
version=self.version,
|
||||
storage_quota_use=self.storage_quota_use,
|
||||
storage_quota=self.storage_quota,
|
||||
append_only=self.append_only,
|
||||
)
|
||||
info = dict(id=self.id, version=self.version, append_only=self.append_only)
|
||||
return info
|
||||
|
||||
def check(self, repair=False, max_duration=0):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import argparse
|
||||
import pytest
|
||||
|
||||
from ...helpers import parse_storage_quota
|
||||
from . import Archiver, RK_ENCRYPTION, cmd
|
||||
|
||||
|
||||
|
|
@ -187,9 +186,3 @@ class TestCommonOptions:
|
|||
}
|
||||
|
||||
assert parse_vars_from_line(*line) == result
|
||||
|
||||
|
||||
def test_parse_storage_quota():
|
||||
assert parse_storage_quota("50M") == 50 * 1000**2
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
parse_storage_quota("5M")
|
||||
|
|
|
|||
|
|
@ -1051,15 +1051,7 @@ def test_remote_borg_cmd(remote_repository):
|
|||
"--debug-topic=borg.debug.repository_compaction",
|
||||
]
|
||||
args = _get_mock_args()
|
||||
args.storage_quota = 0
|
||||
assert remote_repository.borg_cmd(args, testing=False) == ["borg", "serve", "--info"]
|
||||
args.storage_quota = 314159265
|
||||
assert remote_repository.borg_cmd(args, testing=False) == [
|
||||
"borg",
|
||||
"serve",
|
||||
"--info",
|
||||
"--storage-quota=314159265",
|
||||
]
|
||||
args.rsh = "ssh -i foo"
|
||||
remote_repository._args = args
|
||||
assert remote_repository.ssh_cmd(Location("ssh://example.com/foo")) == ["ssh", "-i", "foo", "example.com"]
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class TestSleepingBandwidthLimiter:
|
|||
|
||||
now = 100
|
||||
|
||||
it = SleepingBandwidthLimiter(100)
|
||||
it = SleepingBandwidthLimiter(100) # bandwidth quota
|
||||
|
||||
# all fits
|
||||
self.expect_write(5, b"test")
|
||||
|
|
|
|||
|
|
@ -263,15 +263,7 @@ def test_remote_borg_cmd(remote_repository):
|
|||
"--debug-topic=borg.debug.repository_compaction",
|
||||
]
|
||||
args = _get_mock_args()
|
||||
args.storage_quota = 0
|
||||
assert remote_repository.borg_cmd(args, testing=False) == ["borg", "serve", "--info"]
|
||||
args.storage_quota = 314159265
|
||||
assert remote_repository.borg_cmd(args, testing=False) == [
|
||||
"borg",
|
||||
"serve",
|
||||
"--info",
|
||||
"--storage-quota=314159265",
|
||||
]
|
||||
args.rsh = "ssh -i foo"
|
||||
remote_repository._args = args
|
||||
assert remote_repository.ssh_cmd(Location("ssh://example.com/foo")) == ["ssh", "-i", "foo", "example.com"]
|
||||
|
|
|
|||
Loading…
Reference in a new issue