1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2024-12-23 00:07:38 +00:00

Adding used storage quota to borg info (#7121)

This commit is contained in:
Franco Ayala 2022-12-23 21:55:47 -03:00 committed by GitHub
parent edb28691d5
commit ab465a75d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 19 deletions

View file

@ -3,7 +3,7 @@
from ._common import with_repository
from ..constants import * # NOQA
from ..helpers import bin_to_hex, json_print, basic_json_data
from ..helpers import bin_to_hex, json_print, basic_json_data, format_file_size
from ..manifest import Manifest
from ..logger import create_logger
@ -30,7 +30,7 @@ def do_rinfo(self, args, repository, manifest, cache):
encryption += "\nKey file: %s" % key.find_key()
info["encryption"] = encryption
print(
output = (
textwrap.dedent(
"""
Repository ID: {id}
@ -38,8 +38,6 @@ def do_rinfo(self, args, repository, manifest, cache):
Repository version: {version}
Append only: {append_only}
{encryption}
Cache: {cache.path}
Security dir: {security_dir}
"""
)
.strip()
@ -48,9 +46,31 @@ def do_rinfo(self, args, repository, manifest, cache):
location=repository._location.canonical_path(),
version=repository.version,
append_only=repository.append_only,
**info,
encryption=info["encryption"],
)
)
response = repository.info()
storage_quota = response["storage_quota"]
used = format_file_size(response["storage_quota_use"])
output += f"Storage quota: {used} used"
if storage_quota:
output += f" out of {format_file_size(storage_quota)}"
output += "\n"
output += (
textwrap.dedent(
"""
Cache: {cache.path}
Security dir: {security_dir}
"""
)
.strip()
.format(**info)
)
print(output)
print(str(cache))
return self.exit_code

View file

@ -493,9 +493,22 @@ def open(self, path, exclusive, lock_wait=None, lock=True):
self.id = unhexlify(self.config.get("repository", "id").strip())
self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
def _load_hints(self):
if (transaction_id := self.get_transaction_id()) is None:
# self is a fresh repo, so transaction_id is None and there is no hints file
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)"""
return dict(id=self.id, version=self.version, append_only=self.append_only)
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):
if self.lock:
@ -512,7 +525,6 @@ def commit(self, compact=True, threshold=0.1):
self.rollback()
raise exception
self.check_free_space()
self.log_storage_quota()
segment = self.io.write_commit()
self.segments.setdefault(segment, 0)
self.compact[segment] += LoggedIO.header_fmt.size
@ -556,6 +568,12 @@ def open_index(self, transaction_id, auto_recover=True):
self.commit(compact=False)
return self.open_index(self.get_transaction_id())
def _unpack_hints(self, transaction_id):
hints_path = os.path.join(self.path, "hints.%d" % transaction_id)
integrity_data = self._read_integrity(transaction_id, "hints")
with IntegrityCheckedFile(hints_path, write=False, integrity_data=integrity_data) as fd:
return msgpack.unpack(fd)
def prepare_txn(self, transaction_id, do_cleanup=True):
self._active_txn = True
if self.do_lock and not self.lock.got_exclusive_lock():
@ -592,10 +610,8 @@ def prepare_txn(self, transaction_id, do_cleanup=True):
self.io.cleanup(transaction_id)
hints_path = os.path.join(self.path, "hints.%d" % transaction_id)
index_path = os.path.join(self.path, "index.%d" % transaction_id)
integrity_data = self._read_integrity(transaction_id, "hints")
try:
with IntegrityCheckedFile(hints_path, write=False, integrity_data=integrity_data) as fd:
hints = msgpack.unpack(fd)
hints = self._unpack_hints(transaction_id)
except (msgpack.UnpackException, FileNotFoundError, FileIntegrityError) as e:
logger.warning("Repository hints file missing or corrupted, trying to recover: %s", e)
if not isinstance(e, FileNotFoundError):
@ -622,7 +638,6 @@ def prepare_txn(self, transaction_id, do_cleanup=True):
self.compact = FreeSpace(hints["compact"])
self.storage_quota_use = hints.get("storage_quota_use", 0)
self.shadow_index = hints.get("shadow_index", {})
self.log_storage_quota()
# Drop uncommitted segments in the shadow index
for key, shadowed_segments in self.shadow_index.items():
for segment in list(shadowed_segments):
@ -762,14 +777,6 @@ def check_free_space(self):
formatted_free = format_file_size(free_space)
raise self.InsufficientFreeSpaceError(formatted_required, formatted_free)
def log_storage_quota(self):
if self.storage_quota:
logger.info(
"Storage quota: %s out of %s used.",
format_file_size(self.storage_quota_use),
format_file_size(self.storage_quota),
)
def compact_segments(self, threshold):
"""Compact sparse segments by copying data into new segments"""
if not self.compact:

View file

@ -1,4 +1,5 @@
import json
from random import randbytes
import unittest
from ...constants import * # NOQA
@ -36,6 +37,20 @@ def test_info_json(self):
assert all(isinstance(o, int) for o in stats.values())
assert all(key in stats for key in ("total_chunks", "total_size", "total_unique_chunks", "unique_size"))
def test_info_on_repository_with_storage_quota(self):
self.create_regular_file("file1", contents=randbytes(1000 * 1000))
self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION, "--storage-quota=1G")
self.cmd(f"--repo={self.repository_location}", "create", "test", "input")
info_repo = self.cmd(f"--repo={self.repository_location}", "rinfo")
assert "Storage quota: 1.00 MB used out of 1.00 GB" in info_repo
def test_info_on_repository_without_storage_quota(self):
self.create_regular_file("file1", contents=randbytes(1000 * 1000))
self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION)
self.cmd(f"--repo={self.repository_location}", "create", "test", "input")
info_repo = self.cmd(f"--repo={self.repository_location}", "rinfo")
assert "Storage quota: 1.00 MB used" in info_repo
class RemoteArchiverTestCase(RemoteArchiverTestCaseBase, ArchiverTestCase):
"""run the same tests, but with a remote repository"""