1
0
Fork 0
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:
Jonas Borgström 2014-03-19 22:32:07 +01:00
parent 839dd94a59
commit 8a1ebe0112
5 changed files with 37 additions and 23 deletions

View file

@ -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
------------

View file

@ -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):

View file

@ -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,

View file

@ -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:

View file

@ -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):