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:
parent
edb28691d5
commit
ab465a75d4
3 changed files with 61 additions and 19 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"""
|
||||
|
|
Loading…
Reference in a new issue