mirror of
https://github.com/borgbackup/borg.git
synced 2025-03-19 10:25:49 +00:00
Added '--stats' option to attic prune and attic delete
This commit is contained in:
parent
839dd94a59
commit
8a1ebe0112
5 changed files with 37 additions and 23 deletions
1
CHANGES
1
CHANGES
|
@ -10,6 +10,7 @@ Version 0.12
|
|||
|
||||
- Include "all archives" size information in "--stats" output. (#54)
|
||||
- Switch to SI units (Power of 1000 instead 1024) when printing file sizes
|
||||
- Added "--stats" option to 'attic delete' and 'attic prune'
|
||||
|
||||
Version 0.11
|
||||
------------
|
||||
|
|
|
@ -171,7 +171,7 @@ class Archive:
|
|||
def write_checkpoint(self):
|
||||
self.save(self.checkpoint_name)
|
||||
del self.manifest.archives[self.checkpoint_name]
|
||||
self.cache.chunk_decref(self.id)
|
||||
self.cache.chunk_decref(self.id, self.stats)
|
||||
|
||||
def save(self, name=None):
|
||||
name = name or self.name
|
||||
|
@ -316,17 +316,17 @@ class Archive:
|
|||
elif not symlink:
|
||||
os.utime(path, (item[b'mtime'] / 10**9, item[b'mtime'] / 10**9))
|
||||
|
||||
def delete(self):
|
||||
def delete(self, stats):
|
||||
unpacker = msgpack.Unpacker(use_list=False)
|
||||
for id_, data in zip(self.metadata[b'items'], self.repository.get_many(self.metadata[b'items'])):
|
||||
unpacker.feed(self.key.decrypt(id_, data))
|
||||
self.cache.chunk_decref(id_)
|
||||
for items_id, data in zip(self.metadata[b'items'], self.repository.get_many(self.metadata[b'items'])):
|
||||
unpacker.feed(self.key.decrypt(items_id, data))
|
||||
self.cache.chunk_decref(items_id, stats)
|
||||
for item in unpacker:
|
||||
if b'chunks' in item:
|
||||
for chunk_id, size, csize in item[b'chunks']:
|
||||
self.cache.chunk_decref(chunk_id)
|
||||
self.cache.chunk_decref(chunk_id, stats)
|
||||
|
||||
self.cache.chunk_decref(self.id)
|
||||
self.cache.chunk_decref(self.id, stats)
|
||||
del self.manifest.archives[self.name]
|
||||
|
||||
def stat_attrs(self, st, path):
|
||||
|
|
|
@ -16,7 +16,7 @@ from attic.key import key_creator
|
|||
from attic.helpers import Error, location_validator, format_time, \
|
||||
format_file_mode, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \
|
||||
get_cache_dir, get_keys_dir, format_timedelta, prune_within, prune_split, \
|
||||
Manifest, remove_surrogates, update_excludes, format_archive, check_extension_modules
|
||||
Manifest, remove_surrogates, update_excludes, format_archive, check_extension_modules, Statistics
|
||||
from attic.remote import RepositoryServer, RemoteRepository
|
||||
|
||||
|
||||
|
@ -136,7 +136,8 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
print('Start time: %s' % t0.strftime('%c'))
|
||||
print('End time: %s' % t.strftime('%c'))
|
||||
print('Duration: %s' % format_timedelta(diff))
|
||||
archive.stats.print_(cache)
|
||||
print('Number of files: %d' % archive.stats.nfiles)
|
||||
archive.stats.print_('This archive:', cache)
|
||||
print('-' * 78)
|
||||
return self.exit_code
|
||||
|
||||
|
@ -219,10 +220,13 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
manifest, key = Manifest.load(repository)
|
||||
cache = Cache(repository, key, manifest)
|
||||
archive = Archive(repository, key, manifest, args.archive.archive, cache=cache)
|
||||
archive.delete()
|
||||
stats = Statistics()
|
||||
archive.delete(stats)
|
||||
manifest.write()
|
||||
repository.commit()
|
||||
cache.commit()
|
||||
if args.stats:
|
||||
stats.print_('Deleted data:', cache)
|
||||
return self.exit_code
|
||||
|
||||
def do_mount(self, args):
|
||||
|
@ -300,7 +304,8 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
print('Username:', archive.metadata[b'username'])
|
||||
print('Time: %s' % to_localtime(archive.ts).strftime('%c'))
|
||||
print('Command line:', remove_surrogates(' '.join(archive.metadata[b'cmdline'])))
|
||||
stats.print_(cache)
|
||||
print('Number of files: %d' % archive.stats.nfiles)
|
||||
stats.print_('This archive:', cache)
|
||||
return self.exit_code
|
||||
|
||||
def do_prune(self, args):
|
||||
|
@ -333,7 +338,7 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
|
||||
keep.sort(key=attrgetter('ts'), reverse=True)
|
||||
to_delete = [a for a in archives if a not in keep]
|
||||
|
||||
stats = Statistics()
|
||||
for archive in keep:
|
||||
self.print_verbose('Keeping archive: %s' % format_archive(archive))
|
||||
for archive in to_delete:
|
||||
|
@ -341,11 +346,13 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
self.print_verbose('Would prune: %s' % format_archive(archive))
|
||||
else:
|
||||
self.print_verbose('Pruning archive: %s' % format_archive(archive))
|
||||
archive.delete()
|
||||
archive.delete(stats)
|
||||
if to_delete and not args.dry_run:
|
||||
manifest.write()
|
||||
repository.commit()
|
||||
cache.commit()
|
||||
if args.stats:
|
||||
stats.print_('Deleted data:', cache)
|
||||
return self.exit_code
|
||||
|
||||
helptext = {}
|
||||
|
@ -530,6 +537,9 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
subparser = subparsers.add_parser('delete', parents=[common_parser],
|
||||
description=self.do_delete.__doc__)
|
||||
subparser.set_defaults(func=self.do_delete)
|
||||
subparser.add_argument('-s', '--stats', dest='stats',
|
||||
action='store_true', default=False,
|
||||
help='print statistics for the deleted archive')
|
||||
subparser.add_argument('archive', metavar='ARCHIVE',
|
||||
type=location_validator(archive=True),
|
||||
help='archive to delete')
|
||||
|
@ -586,6 +596,9 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
subparser.add_argument('-n', '--dry-run', dest='dry_run',
|
||||
default=False, action='store_true',
|
||||
help='do not change repository')
|
||||
subparser.add_argument('-s', '--stats', dest='stats',
|
||||
action='store_true', default=False,
|
||||
help='print statistics for the deleted archive')
|
||||
subparser.add_argument('--keep-within', dest='within', type=str, metavar='WITHIN',
|
||||
help='keep all archives within this time interval')
|
||||
subparser.add_argument('-H', '--keep-hourly', dest='hourly', type=int, default=0,
|
||||
|
|
|
@ -193,15 +193,17 @@ class Cache(object):
|
|||
stats.update(size, csize, False)
|
||||
return id, size, csize
|
||||
|
||||
def chunk_decref(self, id):
|
||||
def chunk_decref(self, id, stats):
|
||||
if not self.txn_active:
|
||||
self.begin_txn()
|
||||
count, size, csize = self.chunks[id]
|
||||
if count == 1:
|
||||
del self.chunks[id]
|
||||
self.repository.delete(id, wait=False)
|
||||
stats.update(-size, -csize, True)
|
||||
else:
|
||||
self.chunks[id] = (count - 1, size, csize)
|
||||
stats.update(-size, -csize, False)
|
||||
|
||||
def file_known_and_unchanged(self, path_hash, st):
|
||||
if self.files is None:
|
||||
|
|
|
@ -147,16 +147,14 @@ class Statistics:
|
|||
if unique:
|
||||
self.usize += csize
|
||||
|
||||
def print_(self, cache):
|
||||
def print_(self, label, cache):
|
||||
total_size, total_csize, unique_size, unique_csize = cache.chunks.summarize()
|
||||
print('Number of files: %d' % self.nfiles)
|
||||
print()
|
||||
print(' Original size Compressed size Deduplicated size')
|
||||
print('This archive: %20s %20s %20s' % (format_file_size(self.osize), format_file_size(self.csize), format_file_size(self.usize)))
|
||||
print('%-15s %20s %20s %20s' % (label, format_file_size(self.osize), format_file_size(self.csize), format_file_size(self.usize)))
|
||||
print('All archives: %20s %20s %20s' % (format_file_size(total_size), format_file_size(total_csize), format_file_size(unique_csize)))
|
||||
|
||||
|
||||
|
||||
def get_keys_dir():
|
||||
"""Determine where to repository keys and cache"""
|
||||
return os.environ.get('ATTIC_KEYS_DIR',
|
||||
|
@ -296,16 +294,16 @@ def format_file_mode(mod):
|
|||
def format_file_size(v):
|
||||
"""Format file size into a human friendly format
|
||||
"""
|
||||
if v > 10**12:
|
||||
if abs(v) > 10**12:
|
||||
return '%.2f TB' % (v / 10**12)
|
||||
elif v > 10**9:
|
||||
elif abs(v) > 10**9:
|
||||
return '%.2f GB' % (v / 10**9)
|
||||
elif v > 10**6:
|
||||
elif abs(v) > 10**6:
|
||||
return '%.2f MB' % (v / 10**6)
|
||||
elif v > 10**3:
|
||||
elif abs(v) > 10**3:
|
||||
return '%.2f kB' % (v / 10**3)
|
||||
else:
|
||||
return '%d B ' % v
|
||||
return '%d B' % v
|
||||
|
||||
|
||||
def format_archive(archive):
|
||||
|
|
Loading…
Add table
Reference in a new issue